@signaltree/guardrails 4.0.16 → 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.
- package/dist/factories/index.d.ts +1 -48
- package/dist/factories/index.js +15 -810
- package/dist/{index.js → lib/guardrails.js} +94 -262
- package/dist/lib/rules.js +62 -0
- package/dist/noop.d.ts +1 -19
- package/dist/noop.js +13 -20
- package/package.json +29 -20
- package/src/factories/index.d.ts +20 -0
- package/src/index.d.ts +4 -0
- package/src/lib/guardrails.d.ts +3 -0
- package/src/lib/rules.d.ts +8 -0
- package/src/lib/types.d.ts +138 -0
- package/src/noop.d.ts +11 -0
- package/dist/factories/index.cjs +0 -922
- package/dist/factories/index.cjs.map +0 -1
- package/dist/factories/index.d.cts +0 -48
- package/dist/factories/index.js.map +0 -1
- package/dist/index.cjs +0 -783
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -44
- package/dist/index.d.ts +0 -44
- package/dist/index.js.map +0 -1
- package/dist/noop.cjs +0 -31
- package/dist/noop.cjs.map +0 -1
- package/dist/noop.d.cts +0 -19
- package/dist/noop.js.map +0 -1
- package/dist/types-DfZ9n1yX.d.cts +0 -255
- package/dist/types-DfZ9n1yX.d.ts +0 -255
package/dist/factories/index.cjs
DELETED
|
@@ -1,922 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
-
|
|
6
|
-
// src/lib/guardrails.ts
|
|
7
|
-
function isFunction(value) {
|
|
8
|
-
return typeof value === "function";
|
|
9
|
-
}
|
|
10
|
-
__name(isFunction, "isFunction");
|
|
11
|
-
function isString(value) {
|
|
12
|
-
return typeof value === "string";
|
|
13
|
-
}
|
|
14
|
-
__name(isString, "isString");
|
|
15
|
-
function isObjectLike(value) {
|
|
16
|
-
return typeof value === "object" && value !== null;
|
|
17
|
-
}
|
|
18
|
-
__name(isObjectLike, "isObjectLike");
|
|
19
|
-
function resolveEnabledFlag(option) {
|
|
20
|
-
if (option === void 0) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
if (isFunction(option)) {
|
|
24
|
-
try {
|
|
25
|
-
return option();
|
|
26
|
-
} catch {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return option;
|
|
31
|
-
}
|
|
32
|
-
__name(resolveEnabledFlag, "resolveEnabledFlag");
|
|
33
|
-
function supportsMiddleware(tree) {
|
|
34
|
-
const candidate = tree;
|
|
35
|
-
return isFunction(candidate.addTap) && isFunction(candidate.removeTap);
|
|
36
|
-
}
|
|
37
|
-
__name(supportsMiddleware, "supportsMiddleware");
|
|
38
|
-
function tryStructuredClone(value) {
|
|
39
|
-
const cloneFn = globalThis.structuredClone;
|
|
40
|
-
if (isFunction(cloneFn)) {
|
|
41
|
-
try {
|
|
42
|
-
return cloneFn(value);
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
|
-
__name(tryStructuredClone, "tryStructuredClone");
|
|
49
|
-
function isDevEnvironment() {
|
|
50
|
-
if (__DEV__ !== void 0) return __DEV__;
|
|
51
|
-
if (process?.env?.["NODE_ENV"] === "production") return false;
|
|
52
|
-
if (ngDevMode != null) return Boolean(ngDevMode);
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
__name(isDevEnvironment, "isDevEnvironment");
|
|
56
|
-
var MAX_TIMING_SAMPLES = 1e3;
|
|
57
|
-
var RECOMPUTATION_WINDOW_MS = 1e3;
|
|
58
|
-
function withGuardrails(config = {}) {
|
|
59
|
-
return (tree) => {
|
|
60
|
-
const enabled = resolveEnabledFlag(config.enabled);
|
|
61
|
-
if (!isDevEnvironment() || !enabled) {
|
|
62
|
-
return tree;
|
|
63
|
-
}
|
|
64
|
-
if (!supportsMiddleware(tree)) {
|
|
65
|
-
console.warn("[Guardrails] Tree does not expose middleware hooks; guardrails disabled.");
|
|
66
|
-
return tree;
|
|
67
|
-
}
|
|
68
|
-
const stats = createRuntimeStats();
|
|
69
|
-
const context = {
|
|
70
|
-
tree,
|
|
71
|
-
config,
|
|
72
|
-
stats,
|
|
73
|
-
issues: [],
|
|
74
|
-
hotPaths: [],
|
|
75
|
-
currentUpdate: null,
|
|
76
|
-
suppressed: false,
|
|
77
|
-
timings: [],
|
|
78
|
-
hotPathData: /* @__PURE__ */ new Map(),
|
|
79
|
-
issueMap: /* @__PURE__ */ new Map(),
|
|
80
|
-
signalUsage: /* @__PURE__ */ new Map(),
|
|
81
|
-
memoryHistory: [],
|
|
82
|
-
recomputationLog: [],
|
|
83
|
-
disposed: false
|
|
84
|
-
};
|
|
85
|
-
const middlewareId = `guardrails:${config.treeId ?? "tree"}:${Math.random().toString(36).slice(2)}`;
|
|
86
|
-
context.middlewareId = middlewareId;
|
|
87
|
-
const middleware = createGuardrailsMiddleware(context);
|
|
88
|
-
tree.addTap(middleware);
|
|
89
|
-
const stopMonitoring = startMonitoring(context);
|
|
90
|
-
const teardown = /* @__PURE__ */ __name(() => {
|
|
91
|
-
if (context.disposed) return;
|
|
92
|
-
context.disposed = true;
|
|
93
|
-
stopMonitoring();
|
|
94
|
-
try {
|
|
95
|
-
tree.removeTap(middlewareId);
|
|
96
|
-
} catch {
|
|
97
|
-
}
|
|
98
|
-
}, "teardown");
|
|
99
|
-
const originalDestroy = tree.destroy?.bind(tree);
|
|
100
|
-
tree.destroy = () => {
|
|
101
|
-
teardown();
|
|
102
|
-
if (originalDestroy) {
|
|
103
|
-
originalDestroy();
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
tree["__guardrails"] = createAPI(context, teardown);
|
|
107
|
-
return tree;
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
__name(withGuardrails, "withGuardrails");
|
|
111
|
-
function createRuntimeStats() {
|
|
112
|
-
return {
|
|
113
|
-
updateCount: 0,
|
|
114
|
-
totalUpdateTime: 0,
|
|
115
|
-
avgUpdateTime: 0,
|
|
116
|
-
p50UpdateTime: 0,
|
|
117
|
-
p95UpdateTime: 0,
|
|
118
|
-
p99UpdateTime: 0,
|
|
119
|
-
maxUpdateTime: 0,
|
|
120
|
-
recomputationCount: 0,
|
|
121
|
-
recomputationsPerSecond: 0,
|
|
122
|
-
signalCount: 0,
|
|
123
|
-
signalRetention: 0,
|
|
124
|
-
unreadSignalCount: 0,
|
|
125
|
-
memoryGrowthRate: 0,
|
|
126
|
-
hotPathCount: 0,
|
|
127
|
-
violationCount: 0
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
__name(createRuntimeStats, "createRuntimeStats");
|
|
131
|
-
function createGuardrailsMiddleware(context) {
|
|
132
|
-
return {
|
|
133
|
-
id: context.middlewareId ?? "guardrails",
|
|
134
|
-
before: /* @__PURE__ */ __name((action, payload, state) => {
|
|
135
|
-
if (context.suppressed) {
|
|
136
|
-
context.currentUpdate = null;
|
|
137
|
-
return !context.disposed;
|
|
138
|
-
}
|
|
139
|
-
const metadata = extractMetadata(payload);
|
|
140
|
-
if (shouldSuppressUpdate(context, metadata)) {
|
|
141
|
-
context.currentUpdate = null;
|
|
142
|
-
return !context.disposed;
|
|
143
|
-
}
|
|
144
|
-
const details = collectUpdateDetails(payload, state);
|
|
145
|
-
context.currentUpdate = {
|
|
146
|
-
action,
|
|
147
|
-
startTime: performance.now(),
|
|
148
|
-
metadata,
|
|
149
|
-
details
|
|
150
|
-
};
|
|
151
|
-
for (const detail of details) {
|
|
152
|
-
analyzePreUpdate(context, detail, metadata);
|
|
153
|
-
}
|
|
154
|
-
return !context.disposed;
|
|
155
|
-
}, "before"),
|
|
156
|
-
after: /* @__PURE__ */ __name((_action, _payload, _previousState, newState) => {
|
|
157
|
-
const pending = context.currentUpdate;
|
|
158
|
-
if (!pending) return;
|
|
159
|
-
const duration = Math.max(0, performance.now() - pending.startTime);
|
|
160
|
-
const timestamp = Date.now();
|
|
161
|
-
const recomputations = Math.max(0, pending.details.length - 1);
|
|
162
|
-
updateTimingStats(context, duration);
|
|
163
|
-
for (const [index, detail] of pending.details.entries()) {
|
|
164
|
-
const latest = getValueAtPath(newState, detail.segments);
|
|
165
|
-
const diffRatio = calculateDiffRatio(detail.oldValue, latest);
|
|
166
|
-
analyzePostUpdate(context, detail, duration, diffRatio, index === 0);
|
|
167
|
-
trackHotPath(context, detail.path, duration);
|
|
168
|
-
trackSignalUsage(context, detail.path, timestamp);
|
|
169
|
-
}
|
|
170
|
-
updateSignalStats(context, timestamp);
|
|
171
|
-
recordRecomputations(context, recomputations, timestamp);
|
|
172
|
-
context.currentUpdate = null;
|
|
173
|
-
}, "after")
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
__name(createGuardrailsMiddleware, "createGuardrailsMiddleware");
|
|
177
|
-
function updatePercentiles(context) {
|
|
178
|
-
if (context.timings.length === 0) return;
|
|
179
|
-
const sorted = [
|
|
180
|
-
...context.timings
|
|
181
|
-
].sort((a, b) => a - b);
|
|
182
|
-
context.stats.p50UpdateTime = sorted[Math.floor(sorted.length * 0.5)] || 0;
|
|
183
|
-
context.stats.p95UpdateTime = sorted[Math.floor(sorted.length * 0.95)] || 0;
|
|
184
|
-
context.stats.p99UpdateTime = sorted[Math.floor(sorted.length * 0.99)] || 0;
|
|
185
|
-
}
|
|
186
|
-
__name(updatePercentiles, "updatePercentiles");
|
|
187
|
-
function calculateDiffRatio(oldValue, newValue) {
|
|
188
|
-
if (!isComparableRecord(oldValue) || !isComparableRecord(newValue)) {
|
|
189
|
-
return Object.is(oldValue, newValue) ? 0 : 1;
|
|
190
|
-
}
|
|
191
|
-
if (oldValue === newValue) return 0;
|
|
192
|
-
const oldKeys = new Set(Object.keys(oldValue));
|
|
193
|
-
const newKeys = new Set(Object.keys(newValue));
|
|
194
|
-
const allKeys = /* @__PURE__ */ new Set([
|
|
195
|
-
...oldKeys,
|
|
196
|
-
...newKeys
|
|
197
|
-
]);
|
|
198
|
-
let changed = 0;
|
|
199
|
-
for (const key of allKeys) {
|
|
200
|
-
if (!oldKeys.has(key) || !newKeys.has(key) || oldValue[key] !== newValue[key]) {
|
|
201
|
-
changed++;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return allKeys.size === 0 ? 0 : changed / allKeys.size;
|
|
205
|
-
}
|
|
206
|
-
__name(calculateDiffRatio, "calculateDiffRatio");
|
|
207
|
-
function analyzePreUpdate(context, detail, metadata) {
|
|
208
|
-
if (!context.config.customRules) return;
|
|
209
|
-
for (const rule of context.config.customRules) {
|
|
210
|
-
evaluateRule(context, rule, {
|
|
211
|
-
path: detail.segments,
|
|
212
|
-
value: detail.newValue,
|
|
213
|
-
oldValue: detail.oldValue,
|
|
214
|
-
metadata,
|
|
215
|
-
tree: context.tree,
|
|
216
|
-
stats: context.stats
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
__name(analyzePreUpdate, "analyzePreUpdate");
|
|
221
|
-
function analyzePostUpdate(context, detail, duration, diffRatio, isPrimary) {
|
|
222
|
-
if (isPrimary && context.config.budgets?.maxUpdateTime && duration > context.config.budgets.maxUpdateTime) {
|
|
223
|
-
addIssue(context, {
|
|
224
|
-
type: "budget",
|
|
225
|
-
severity: "error",
|
|
226
|
-
message: `Update took ${duration.toFixed(2)}ms (budget: ${context.config.budgets.maxUpdateTime}ms)`,
|
|
227
|
-
path: detail.path,
|
|
228
|
-
count: 1
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
const minDiff = context.config.analysis?.minDiffForParentReplace ?? 0.8;
|
|
232
|
-
if (context.config.analysis?.warnParentReplace && diffRatio > minDiff) {
|
|
233
|
-
addIssue(context, {
|
|
234
|
-
type: "analysis",
|
|
235
|
-
severity: "warning",
|
|
236
|
-
message: `High diff ratio (${(diffRatio * 100).toFixed(0)}%) - consider scoped updates`,
|
|
237
|
-
path: detail.path,
|
|
238
|
-
count: 1,
|
|
239
|
-
diffRatio
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
__name(analyzePostUpdate, "analyzePostUpdate");
|
|
244
|
-
function trackHotPath(context, path, duration) {
|
|
245
|
-
if (!context.config.hotPaths?.enabled) return;
|
|
246
|
-
const pathKey = Array.isArray(path) ? path.join(".") : path;
|
|
247
|
-
const now = Date.now();
|
|
248
|
-
const windowMs = context.config.hotPaths.windowMs || 1e3;
|
|
249
|
-
let data = context.hotPathData.get(pathKey);
|
|
250
|
-
if (!data) {
|
|
251
|
-
data = {
|
|
252
|
-
count: 0,
|
|
253
|
-
lastUpdate: now,
|
|
254
|
-
durations: []
|
|
255
|
-
};
|
|
256
|
-
context.hotPathData.set(pathKey, data);
|
|
257
|
-
}
|
|
258
|
-
if (now - data.lastUpdate > windowMs) {
|
|
259
|
-
data.count = 0;
|
|
260
|
-
data.durations = [];
|
|
261
|
-
}
|
|
262
|
-
data.count++;
|
|
263
|
-
data.durations.push(duration);
|
|
264
|
-
data.lastUpdate = now;
|
|
265
|
-
const threshold = context.config.hotPaths.threshold || 10;
|
|
266
|
-
const updatesPerSecond = data.count / windowMs * 1e3;
|
|
267
|
-
if (updatesPerSecond > threshold) {
|
|
268
|
-
const sorted = [
|
|
269
|
-
...data.durations
|
|
270
|
-
].sort((a, b) => a - b);
|
|
271
|
-
const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0;
|
|
272
|
-
const avg = data.durations.reduce((sum, d) => sum + d, 0) / data.durations.length;
|
|
273
|
-
updateHotPath(context, {
|
|
274
|
-
path: pathKey,
|
|
275
|
-
updatesPerSecond,
|
|
276
|
-
heatScore: Math.min(100, updatesPerSecond / threshold * 50),
|
|
277
|
-
downstreamEffects: 0,
|
|
278
|
-
avgDuration: avg,
|
|
279
|
-
p95Duration: p95
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
__name(trackHotPath, "trackHotPath");
|
|
284
|
-
function trackSignalUsage(context, path, timestamp) {
|
|
285
|
-
const key = Array.isArray(path) ? path.join(".") : path;
|
|
286
|
-
const entry = context.signalUsage.get(key) ?? {
|
|
287
|
-
updates: 0,
|
|
288
|
-
lastSeen: timestamp
|
|
289
|
-
};
|
|
290
|
-
entry.updates += 1;
|
|
291
|
-
entry.lastSeen = timestamp;
|
|
292
|
-
context.signalUsage.set(key, entry);
|
|
293
|
-
}
|
|
294
|
-
__name(trackSignalUsage, "trackSignalUsage");
|
|
295
|
-
function updateSignalStats(context, timestamp) {
|
|
296
|
-
const retentionWindow = context.config.memoryLeaks?.checkInterval ?? 5e3;
|
|
297
|
-
const historyWindow = Math.max(retentionWindow, 1e3);
|
|
298
|
-
const signalCount = context.signalUsage.size;
|
|
299
|
-
context.stats.signalCount = signalCount;
|
|
300
|
-
const staleCount = [
|
|
301
|
-
...context.signalUsage.values()
|
|
302
|
-
].filter((entry) => timestamp - entry.lastSeen > retentionWindow).length;
|
|
303
|
-
context.stats.signalRetention = staleCount;
|
|
304
|
-
context.stats.unreadSignalCount = 0;
|
|
305
|
-
context.memoryHistory.push({
|
|
306
|
-
timestamp,
|
|
307
|
-
count: signalCount
|
|
308
|
-
});
|
|
309
|
-
context.memoryHistory = context.memoryHistory.filter((entry) => timestamp - entry.timestamp <= historyWindow);
|
|
310
|
-
const baseline = context.memoryHistory[0]?.count ?? signalCount;
|
|
311
|
-
const growth = baseline === 0 ? 0 : (signalCount - baseline) / Math.max(1, baseline);
|
|
312
|
-
context.stats.memoryGrowthRate = growth;
|
|
313
|
-
}
|
|
314
|
-
__name(updateSignalStats, "updateSignalStats");
|
|
315
|
-
function recordRecomputations(context, count, timestamp) {
|
|
316
|
-
if (count > 0) {
|
|
317
|
-
context.stats.recomputationCount += count;
|
|
318
|
-
for (let i = 0; i < count; i++) {
|
|
319
|
-
context.recomputationLog.push(timestamp);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (context.recomputationLog.length) {
|
|
323
|
-
context.recomputationLog = context.recomputationLog.filter((value) => timestamp - value <= RECOMPUTATION_WINDOW_MS);
|
|
324
|
-
}
|
|
325
|
-
context.stats.recomputationsPerSecond = context.recomputationLog.length;
|
|
326
|
-
}
|
|
327
|
-
__name(recordRecomputations, "recordRecomputations");
|
|
328
|
-
function updateHotPath(context, hotPath) {
|
|
329
|
-
const existing = context.hotPaths.find((h) => h.path === hotPath.path);
|
|
330
|
-
if (existing) {
|
|
331
|
-
Object.assign(existing, hotPath);
|
|
332
|
-
} else {
|
|
333
|
-
context.hotPaths.push(hotPath);
|
|
334
|
-
const topN = context.config.hotPaths?.topN || 5;
|
|
335
|
-
if (context.hotPaths.length > topN) {
|
|
336
|
-
context.hotPaths.sort((a, b) => b.heatScore - a.heatScore);
|
|
337
|
-
context.hotPaths.length = topN;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
context.stats.hotPathCount = context.hotPaths.length;
|
|
341
|
-
}
|
|
342
|
-
__name(updateHotPath, "updateHotPath");
|
|
343
|
-
function evaluateRule(context, rule, ruleContext) {
|
|
344
|
-
const handleFailure = /* @__PURE__ */ __name(() => {
|
|
345
|
-
const message = typeof rule.message === "function" ? rule.message(ruleContext) : rule.message;
|
|
346
|
-
addIssue(context, {
|
|
347
|
-
type: "rule",
|
|
348
|
-
severity: rule.severity || "warning",
|
|
349
|
-
message,
|
|
350
|
-
path: ruleContext.path.join("."),
|
|
351
|
-
count: 1,
|
|
352
|
-
metadata: {
|
|
353
|
-
rule: rule.name
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
}, "handleFailure");
|
|
357
|
-
try {
|
|
358
|
-
const result = rule.test(ruleContext);
|
|
359
|
-
if (result instanceof Promise) {
|
|
360
|
-
result.then((outcome) => {
|
|
361
|
-
if (!outcome) {
|
|
362
|
-
handleFailure();
|
|
363
|
-
}
|
|
364
|
-
}).catch((error) => {
|
|
365
|
-
console.warn(`[Guardrails] Rule ${rule.name} rejected:`, error);
|
|
366
|
-
});
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (!result) {
|
|
370
|
-
handleFailure();
|
|
371
|
-
}
|
|
372
|
-
} catch (error) {
|
|
373
|
-
console.warn(`[Guardrails] Rule ${rule.name} threw error:`, error);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
__name(evaluateRule, "evaluateRule");
|
|
377
|
-
function addIssue(context, issue) {
|
|
378
|
-
if (context.suppressed) return;
|
|
379
|
-
if (context.config.reporting?.aggregateWarnings !== false) {
|
|
380
|
-
const key = `${issue.type}:${issue.path}:${issue.message}`;
|
|
381
|
-
const existing = context.issueMap.get(key);
|
|
382
|
-
if (existing) {
|
|
383
|
-
existing.count++;
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
context.issueMap.set(key, issue);
|
|
387
|
-
}
|
|
388
|
-
context.issues.push(issue);
|
|
389
|
-
context.stats.violationCount++;
|
|
390
|
-
if (context.config.mode === "throw") {
|
|
391
|
-
throw new Error(`[Guardrails] ${issue.message}`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
__name(addIssue, "addIssue");
|
|
395
|
-
function shouldSuppressUpdate(context, metadata) {
|
|
396
|
-
if (context.suppressed) return true;
|
|
397
|
-
if (!metadata) return false;
|
|
398
|
-
if (metadata.suppressGuardrails && context.config.suppression?.respectMetadata !== false) {
|
|
399
|
-
return true;
|
|
400
|
-
}
|
|
401
|
-
const autoSuppress = new Set(context.config.suppression?.autoSuppress ?? []);
|
|
402
|
-
return [
|
|
403
|
-
metadata.intent,
|
|
404
|
-
metadata.source
|
|
405
|
-
].some((value) => isString(value) && autoSuppress.has(value));
|
|
406
|
-
}
|
|
407
|
-
__name(shouldSuppressUpdate, "shouldSuppressUpdate");
|
|
408
|
-
function startMonitoring(context) {
|
|
409
|
-
const interval = setInterval(() => {
|
|
410
|
-
if (context.disposed) {
|
|
411
|
-
clearInterval(interval);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
checkMemory(context);
|
|
415
|
-
maybeReport(context);
|
|
416
|
-
}, context.config.reporting?.interval || 5e3);
|
|
417
|
-
return () => clearInterval(interval);
|
|
418
|
-
}
|
|
419
|
-
__name(startMonitoring, "startMonitoring");
|
|
420
|
-
function checkMemory(context) {
|
|
421
|
-
if (!context.config.memoryLeaks?.enabled) return;
|
|
422
|
-
const now = Date.now();
|
|
423
|
-
const retentionWindow = context.config.memoryLeaks?.checkInterval ?? 5e3;
|
|
424
|
-
const retentionThreshold = context.config.memoryLeaks?.retentionThreshold ?? 100;
|
|
425
|
-
const growthThreshold = context.config.memoryLeaks?.growthRate ?? 0.2;
|
|
426
|
-
const staleCount = [
|
|
427
|
-
...context.signalUsage.values()
|
|
428
|
-
].filter((entry) => now - entry.lastSeen > retentionWindow).length;
|
|
429
|
-
context.stats.signalRetention = staleCount;
|
|
430
|
-
const exceedsRetention = context.stats.signalRetention > retentionThreshold;
|
|
431
|
-
const exceedsGrowth = context.stats.memoryGrowthRate > growthThreshold;
|
|
432
|
-
if (exceedsRetention || exceedsGrowth) {
|
|
433
|
-
addIssue(context, {
|
|
434
|
-
type: "memory",
|
|
435
|
-
severity: "warning",
|
|
436
|
-
message: `Potential memory leak detected (signals: ${context.stats.signalCount}, growth ${(context.stats.memoryGrowthRate * 100).toFixed(1)}%)`,
|
|
437
|
-
path: "root",
|
|
438
|
-
count: 1,
|
|
439
|
-
metadata: {
|
|
440
|
-
signalCount: context.stats.signalCount,
|
|
441
|
-
growth: context.stats.memoryGrowthRate
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
__name(checkMemory, "checkMemory");
|
|
447
|
-
function maybeReport(context) {
|
|
448
|
-
if (context.config.reporting?.console === false) return;
|
|
449
|
-
const report = generateReport(context);
|
|
450
|
-
if (context.config.reporting?.customReporter) {
|
|
451
|
-
context.config.reporting.customReporter(report);
|
|
452
|
-
}
|
|
453
|
-
if (context.config.reporting?.console && context.issues.length > 0) {
|
|
454
|
-
reportToConsole(report, context.config.reporting.console === "verbose");
|
|
455
|
-
}
|
|
456
|
-
context.issues = [];
|
|
457
|
-
context.issueMap.clear();
|
|
458
|
-
}
|
|
459
|
-
__name(maybeReport, "maybeReport");
|
|
460
|
-
function reportToConsole(report, verbose) {
|
|
461
|
-
console.group("[Guardrails] Performance Report");
|
|
462
|
-
logIssues(report.issues);
|
|
463
|
-
logHotPaths(report.hotPaths);
|
|
464
|
-
if (verbose) {
|
|
465
|
-
logVerboseStats(report);
|
|
466
|
-
}
|
|
467
|
-
console.groupEnd();
|
|
468
|
-
}
|
|
469
|
-
__name(reportToConsole, "reportToConsole");
|
|
470
|
-
function logIssues(issues) {
|
|
471
|
-
if (issues.length === 0) return;
|
|
472
|
-
console.warn(`${issues.length} issues detected:`);
|
|
473
|
-
for (const issue of issues) {
|
|
474
|
-
const prefix = getSeverityPrefix(issue.severity);
|
|
475
|
-
const countSuffix = issue.count > 1 ? ` (x${issue.count})` : "";
|
|
476
|
-
const message = `${prefix} [${issue.type}] ${issue.message}${countSuffix}`;
|
|
477
|
-
console.log(` ${message}`);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
__name(logIssues, "logIssues");
|
|
481
|
-
function logHotPaths(hotPaths) {
|
|
482
|
-
if (hotPaths.length === 0) return;
|
|
483
|
-
console.log(`
|
|
484
|
-
Hot Paths (${hotPaths.length}):`);
|
|
485
|
-
for (const hp of hotPaths) {
|
|
486
|
-
const entry = ` \u{1F525} ${hp.path}: ${hp.updatesPerSecond.toFixed(1)}/s (heat: ${hp.heatScore.toFixed(0)})`;
|
|
487
|
-
console.log(entry);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
__name(logHotPaths, "logHotPaths");
|
|
491
|
-
function logVerboseStats(report) {
|
|
492
|
-
console.log("\nStats:", report.stats);
|
|
493
|
-
console.log("Budgets:", report.budgets);
|
|
494
|
-
}
|
|
495
|
-
__name(logVerboseStats, "logVerboseStats");
|
|
496
|
-
function getSeverityPrefix(severity) {
|
|
497
|
-
if (severity === "error") return "\u274C";
|
|
498
|
-
if (severity === "warning") return "\u26A0\uFE0F";
|
|
499
|
-
return "\u2139\uFE0F";
|
|
500
|
-
}
|
|
501
|
-
__name(getSeverityPrefix, "getSeverityPrefix");
|
|
502
|
-
function generateReport(context) {
|
|
503
|
-
const memoryCurrent = context.stats.signalCount;
|
|
504
|
-
const memoryLimit = context.config.budgets?.maxMemory ?? 50;
|
|
505
|
-
const recomputationCurrent = context.stats.recomputationsPerSecond;
|
|
506
|
-
const recomputationLimit = context.config.budgets?.maxRecomputations ?? 100;
|
|
507
|
-
const budgets = {
|
|
508
|
-
updateTime: createBudgetItem(context.stats.avgUpdateTime, context.config.budgets?.maxUpdateTime || 16),
|
|
509
|
-
memory: createBudgetItem(memoryCurrent, memoryLimit),
|
|
510
|
-
recomputations: createBudgetItem(recomputationCurrent, recomputationLimit)
|
|
511
|
-
};
|
|
512
|
-
return {
|
|
513
|
-
timestamp: Date.now(),
|
|
514
|
-
treeId: context.config.treeId,
|
|
515
|
-
issues: Array.from(context.issueMap.values()),
|
|
516
|
-
hotPaths: context.hotPaths,
|
|
517
|
-
budgets,
|
|
518
|
-
stats: context.stats,
|
|
519
|
-
recommendations: generateRecommendations(context)
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
__name(generateReport, "generateReport");
|
|
523
|
-
function createBudgetItem(current, limit) {
|
|
524
|
-
if (limit <= 0) {
|
|
525
|
-
return {
|
|
526
|
-
current,
|
|
527
|
-
limit,
|
|
528
|
-
usage: 0,
|
|
529
|
-
status: "ok"
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
const usage = current / limit * 100;
|
|
533
|
-
const threshold = 80;
|
|
534
|
-
let status;
|
|
535
|
-
if (usage > 100) {
|
|
536
|
-
status = "exceeded";
|
|
537
|
-
} else if (usage > threshold) {
|
|
538
|
-
status = "warning";
|
|
539
|
-
} else {
|
|
540
|
-
status = "ok";
|
|
541
|
-
}
|
|
542
|
-
return {
|
|
543
|
-
current,
|
|
544
|
-
limit,
|
|
545
|
-
usage,
|
|
546
|
-
status
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
__name(createBudgetItem, "createBudgetItem");
|
|
550
|
-
function generateRecommendations(context) {
|
|
551
|
-
const recommendations = [];
|
|
552
|
-
if (context.hotPaths.length > 0) {
|
|
553
|
-
recommendations.push("Consider batching or debouncing updates to hot paths");
|
|
554
|
-
}
|
|
555
|
-
if (context.stats.avgUpdateTime > 10) {
|
|
556
|
-
recommendations.push("Average update time is high - review update logic");
|
|
557
|
-
}
|
|
558
|
-
return recommendations;
|
|
559
|
-
}
|
|
560
|
-
__name(generateRecommendations, "generateRecommendations");
|
|
561
|
-
function createAPI(context, teardown) {
|
|
562
|
-
return {
|
|
563
|
-
getReport: /* @__PURE__ */ __name(() => generateReport(context), "getReport"),
|
|
564
|
-
getStats: /* @__PURE__ */ __name(() => context.stats, "getStats"),
|
|
565
|
-
suppress: /* @__PURE__ */ __name((fn) => {
|
|
566
|
-
const was = context.suppressed;
|
|
567
|
-
context.suppressed = true;
|
|
568
|
-
try {
|
|
569
|
-
fn();
|
|
570
|
-
} finally {
|
|
571
|
-
context.suppressed = was;
|
|
572
|
-
}
|
|
573
|
-
}, "suppress"),
|
|
574
|
-
dispose: /* @__PURE__ */ __name(() => {
|
|
575
|
-
teardown();
|
|
576
|
-
const finalReport = generateReport(context);
|
|
577
|
-
if (context.config.reporting?.console !== false) {
|
|
578
|
-
console.log("[Guardrails] Final report on disposal:", finalReport);
|
|
579
|
-
}
|
|
580
|
-
if (context.config.reporting?.customReporter) {
|
|
581
|
-
context.config.reporting.customReporter(finalReport);
|
|
582
|
-
}
|
|
583
|
-
}, "dispose")
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
__name(createAPI, "createAPI");
|
|
587
|
-
function extractMetadata(payload) {
|
|
588
|
-
if (!isObjectLike(payload)) return void 0;
|
|
589
|
-
const candidate = payload["metadata"];
|
|
590
|
-
return isObjectLike(candidate) ? candidate : void 0;
|
|
591
|
-
}
|
|
592
|
-
__name(extractMetadata, "extractMetadata");
|
|
593
|
-
function collectUpdateDetails(payload, stateSnapshot) {
|
|
594
|
-
const details = [];
|
|
595
|
-
const visit = /* @__PURE__ */ __name((value, segments, currentState) => {
|
|
596
|
-
const path = segments.length ? segments.join(".") : "root";
|
|
597
|
-
const oldValue = captureValue(currentState);
|
|
598
|
-
if (isObjectLike(value)) {
|
|
599
|
-
details.push({
|
|
600
|
-
path,
|
|
601
|
-
segments: [
|
|
602
|
-
...segments
|
|
603
|
-
],
|
|
604
|
-
newValue: value,
|
|
605
|
-
oldValue
|
|
606
|
-
});
|
|
607
|
-
for (const [key, child] of Object.entries(value)) {
|
|
608
|
-
visit(child, [
|
|
609
|
-
...segments,
|
|
610
|
-
key
|
|
611
|
-
], isObjectLike(currentState) ? currentState[key] : void 0);
|
|
612
|
-
}
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
details.push({
|
|
616
|
-
path,
|
|
617
|
-
segments: [
|
|
618
|
-
...segments
|
|
619
|
-
],
|
|
620
|
-
newValue: value,
|
|
621
|
-
oldValue
|
|
622
|
-
});
|
|
623
|
-
}, "visit");
|
|
624
|
-
if (isObjectLike(payload)) {
|
|
625
|
-
visit(payload, [], stateSnapshot);
|
|
626
|
-
} else {
|
|
627
|
-
details.push({
|
|
628
|
-
path: "root",
|
|
629
|
-
segments: [],
|
|
630
|
-
newValue: payload,
|
|
631
|
-
oldValue: captureValue(stateSnapshot)
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
if (details.length === 0) {
|
|
635
|
-
details.push({
|
|
636
|
-
path: "root",
|
|
637
|
-
segments: [],
|
|
638
|
-
newValue: payload,
|
|
639
|
-
oldValue: captureValue(stateSnapshot)
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
return details;
|
|
643
|
-
}
|
|
644
|
-
__name(collectUpdateDetails, "collectUpdateDetails");
|
|
645
|
-
function getValueAtPath(source, segments) {
|
|
646
|
-
let current = source;
|
|
647
|
-
for (const segment of segments) {
|
|
648
|
-
if (!isObjectLike(current)) {
|
|
649
|
-
return void 0;
|
|
650
|
-
}
|
|
651
|
-
current = current[segment];
|
|
652
|
-
}
|
|
653
|
-
return current;
|
|
654
|
-
}
|
|
655
|
-
__name(getValueAtPath, "getValueAtPath");
|
|
656
|
-
function captureValue(value) {
|
|
657
|
-
return tryStructuredClone(value);
|
|
658
|
-
}
|
|
659
|
-
__name(captureValue, "captureValue");
|
|
660
|
-
function isPlainObject(value) {
|
|
661
|
-
if (!value || typeof value !== "object") return false;
|
|
662
|
-
const proto = Object.getPrototypeOf(value);
|
|
663
|
-
return proto === Object.prototype || proto === null;
|
|
664
|
-
}
|
|
665
|
-
__name(isPlainObject, "isPlainObject");
|
|
666
|
-
function updateTimingStats(context, duration) {
|
|
667
|
-
context.timings.push(duration);
|
|
668
|
-
if (context.timings.length > MAX_TIMING_SAMPLES) {
|
|
669
|
-
context.timings.shift();
|
|
670
|
-
}
|
|
671
|
-
context.stats.updateCount++;
|
|
672
|
-
context.stats.totalUpdateTime += duration;
|
|
673
|
-
context.stats.avgUpdateTime = context.stats.totalUpdateTime / context.stats.updateCount;
|
|
674
|
-
context.stats.maxUpdateTime = Math.max(context.stats.maxUpdateTime, duration);
|
|
675
|
-
updatePercentiles(context);
|
|
676
|
-
}
|
|
677
|
-
__name(updateTimingStats, "updateTimingStats");
|
|
678
|
-
function isComparableRecord(value) {
|
|
679
|
-
return isPlainObject(value);
|
|
680
|
-
}
|
|
681
|
-
__name(isComparableRecord, "isComparableRecord");
|
|
682
|
-
|
|
683
|
-
// src/lib/rules.ts
|
|
684
|
-
var rules = {
|
|
685
|
-
/**
|
|
686
|
-
* Prevents deep nesting beyond specified depth
|
|
687
|
-
*/
|
|
688
|
-
noDeepNesting: /* @__PURE__ */ __name((maxDepth = 5) => ({
|
|
689
|
-
name: "no-deep-nesting",
|
|
690
|
-
description: `Prevents nesting deeper than ${maxDepth} levels`,
|
|
691
|
-
test: /* @__PURE__ */ __name((ctx) => ctx.path.length <= maxDepth, "test"),
|
|
692
|
-
message: /* @__PURE__ */ __name((ctx) => `Path too deep: ${ctx.path.join(".")} (${ctx.path.length} levels, max: ${maxDepth})`, "message"),
|
|
693
|
-
severity: "warning",
|
|
694
|
-
tags: [
|
|
695
|
-
"architecture",
|
|
696
|
-
"complexity"
|
|
697
|
-
]
|
|
698
|
-
}), "noDeepNesting"),
|
|
699
|
-
/**
|
|
700
|
-
* Prevents storing functions in state (breaks serialization)
|
|
701
|
-
*/
|
|
702
|
-
noFunctionsInState: /* @__PURE__ */ __name(() => ({
|
|
703
|
-
name: "no-functions",
|
|
704
|
-
description: "Functions break serialization",
|
|
705
|
-
test: /* @__PURE__ */ __name((ctx) => typeof ctx.value !== "function", "test"),
|
|
706
|
-
message: "Functions cannot be stored in state (breaks serialization)",
|
|
707
|
-
severity: "error",
|
|
708
|
-
tags: [
|
|
709
|
-
"serialization",
|
|
710
|
-
"data"
|
|
711
|
-
]
|
|
712
|
-
}), "noFunctionsInState"),
|
|
713
|
-
/**
|
|
714
|
-
* Prevents cache from being persisted
|
|
715
|
-
*/
|
|
716
|
-
noCacheInPersistence: /* @__PURE__ */ __name(() => ({
|
|
717
|
-
name: "no-cache-persistence",
|
|
718
|
-
description: "Prevent cache from being persisted",
|
|
719
|
-
test: /* @__PURE__ */ __name((ctx) => {
|
|
720
|
-
if (ctx.metadata?.source === "serialization" && ctx.path.includes("cache")) {
|
|
721
|
-
return false;
|
|
722
|
-
}
|
|
723
|
-
return true;
|
|
724
|
-
}, "test"),
|
|
725
|
-
message: "Cache should not be persisted",
|
|
726
|
-
severity: "warning",
|
|
727
|
-
tags: [
|
|
728
|
-
"persistence",
|
|
729
|
-
"cache"
|
|
730
|
-
]
|
|
731
|
-
}), "noCacheInPersistence"),
|
|
732
|
-
/**
|
|
733
|
-
* Limits payload size
|
|
734
|
-
*/
|
|
735
|
-
maxPayloadSize: /* @__PURE__ */ __name((maxKB = 100) => ({
|
|
736
|
-
name: "max-payload-size",
|
|
737
|
-
description: `Limit payload size to ${maxKB}KB`,
|
|
738
|
-
test: /* @__PURE__ */ __name((ctx) => {
|
|
739
|
-
try {
|
|
740
|
-
const size = JSON.stringify(ctx.value).length;
|
|
741
|
-
return size < maxKB * 1024;
|
|
742
|
-
} catch {
|
|
743
|
-
return true;
|
|
744
|
-
}
|
|
745
|
-
}, "test"),
|
|
746
|
-
message: /* @__PURE__ */ __name((ctx) => {
|
|
747
|
-
const size = JSON.stringify(ctx.value).length;
|
|
748
|
-
const kb = (size / 1024).toFixed(1);
|
|
749
|
-
return `Payload size ${kb}KB exceeds limit of ${maxKB}KB`;
|
|
750
|
-
}, "message"),
|
|
751
|
-
severity: "warning",
|
|
752
|
-
tags: [
|
|
753
|
-
"performance",
|
|
754
|
-
"data"
|
|
755
|
-
]
|
|
756
|
-
}), "maxPayloadSize"),
|
|
757
|
-
/**
|
|
758
|
-
* Prevents storing sensitive data
|
|
759
|
-
*/
|
|
760
|
-
noSensitiveData: /* @__PURE__ */ __name((sensitiveKeys = [
|
|
761
|
-
"password",
|
|
762
|
-
"token",
|
|
763
|
-
"secret",
|
|
764
|
-
"apiKey"
|
|
765
|
-
]) => ({
|
|
766
|
-
name: "no-sensitive-data",
|
|
767
|
-
description: "Prevents storing sensitive data",
|
|
768
|
-
test: /* @__PURE__ */ __name((ctx) => {
|
|
769
|
-
return !ctx.path.some((segment) => sensitiveKeys.some((key) => typeof segment === "string" && segment.toLowerCase().includes(key.toLowerCase())));
|
|
770
|
-
}, "test"),
|
|
771
|
-
message: /* @__PURE__ */ __name((ctx) => `Sensitive data detected in path: ${ctx.path.join(".")}`, "message"),
|
|
772
|
-
severity: "error",
|
|
773
|
-
tags: [
|
|
774
|
-
"security",
|
|
775
|
-
"data"
|
|
776
|
-
]
|
|
777
|
-
}), "noSensitiveData")
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
// src/factories/index.ts
|
|
781
|
-
function isGuardrailsConfig(value) {
|
|
782
|
-
return Boolean(value) && typeof value === "object";
|
|
783
|
-
}
|
|
784
|
-
__name(isGuardrailsConfig, "isGuardrailsConfig");
|
|
785
|
-
function resolveGuardrailsConfig(guardrails) {
|
|
786
|
-
if (guardrails === false) {
|
|
787
|
-
return void 0;
|
|
788
|
-
}
|
|
789
|
-
if (isGuardrailsConfig(guardrails)) {
|
|
790
|
-
return guardrails;
|
|
791
|
-
}
|
|
792
|
-
return {
|
|
793
|
-
budgets: {
|
|
794
|
-
maxUpdateTime: 16,
|
|
795
|
-
maxRecomputations: 100
|
|
796
|
-
},
|
|
797
|
-
hotPaths: {
|
|
798
|
-
enabled: true,
|
|
799
|
-
threshold: 10
|
|
800
|
-
},
|
|
801
|
-
reporting: {
|
|
802
|
-
console: true
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
__name(resolveGuardrailsConfig, "resolveGuardrailsConfig");
|
|
807
|
-
function createFeatureTree(signalTree, initial, options) {
|
|
808
|
-
const env = options.env ?? process?.env?.["NODE_ENV"] ?? "production";
|
|
809
|
-
const isDev = env === "development";
|
|
810
|
-
const isTest = env === "test";
|
|
811
|
-
const enhancers = [];
|
|
812
|
-
if (isDev || isTest) {
|
|
813
|
-
const guardrailsConfig = resolveGuardrailsConfig(options.guardrails);
|
|
814
|
-
if (guardrailsConfig) {
|
|
815
|
-
enhancers.push(withGuardrails(guardrailsConfig));
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
if (options.enhancers?.length) {
|
|
819
|
-
enhancers.push(...options.enhancers);
|
|
820
|
-
}
|
|
821
|
-
let tree = signalTree(initial);
|
|
822
|
-
for (const enhancer of enhancers) {
|
|
823
|
-
tree = tree.with(enhancer);
|
|
824
|
-
}
|
|
825
|
-
return tree;
|
|
826
|
-
}
|
|
827
|
-
__name(createFeatureTree, "createFeatureTree");
|
|
828
|
-
function createAngularFeatureTree(signalTree, initial, options) {
|
|
829
|
-
const isDev = Boolean(ngDevMode);
|
|
830
|
-
return createFeatureTree(signalTree, initial, {
|
|
831
|
-
...options,
|
|
832
|
-
env: isDev ? "development" : "production"
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
__name(createAngularFeatureTree, "createAngularFeatureTree");
|
|
836
|
-
function createAppShellTree(signalTree, initial) {
|
|
837
|
-
return createFeatureTree(signalTree, initial, {
|
|
838
|
-
guardrails: {
|
|
839
|
-
budgets: {
|
|
840
|
-
maxUpdateTime: 4,
|
|
841
|
-
maxMemory: 20
|
|
842
|
-
},
|
|
843
|
-
hotPaths: {
|
|
844
|
-
threshold: 5
|
|
845
|
-
},
|
|
846
|
-
customRules: [
|
|
847
|
-
rules.noDeepNesting(3)
|
|
848
|
-
]
|
|
849
|
-
}
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
__name(createAppShellTree, "createAppShellTree");
|
|
853
|
-
function createPerformanceTree(signalTree, initial, name) {
|
|
854
|
-
return createFeatureTree(signalTree, initial, {
|
|
855
|
-
guardrails: {
|
|
856
|
-
budgets: {
|
|
857
|
-
maxUpdateTime: 8,
|
|
858
|
-
maxRecomputations: 200
|
|
859
|
-
},
|
|
860
|
-
hotPaths: {
|
|
861
|
-
threshold: 50
|
|
862
|
-
},
|
|
863
|
-
memoryLeaks: {
|
|
864
|
-
enabled: false
|
|
865
|
-
},
|
|
866
|
-
reporting: {
|
|
867
|
-
interval: 1e4
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
__name(createPerformanceTree, "createPerformanceTree");
|
|
873
|
-
function createFormTree(signalTree, initial, formName) {
|
|
874
|
-
return createFeatureTree(signalTree, initial, {
|
|
875
|
-
guardrails: {
|
|
876
|
-
customRules: [
|
|
877
|
-
rules.noDeepNesting(4),
|
|
878
|
-
rules.maxPayloadSize(50),
|
|
879
|
-
rules.noSensitiveData()
|
|
880
|
-
]
|
|
881
|
-
}
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
__name(createFormTree, "createFormTree");
|
|
885
|
-
function createCacheTree(signalTree, initial) {
|
|
886
|
-
return createFeatureTree(signalTree, initial, {
|
|
887
|
-
guardrails: {
|
|
888
|
-
mode: "silent",
|
|
889
|
-
memoryLeaks: {
|
|
890
|
-
enabled: false
|
|
891
|
-
}
|
|
892
|
-
}});
|
|
893
|
-
}
|
|
894
|
-
__name(createCacheTree, "createCacheTree");
|
|
895
|
-
function createTestTree(signalTree, initial, overrides) {
|
|
896
|
-
return createFeatureTree(signalTree, initial, {
|
|
897
|
-
env: "test",
|
|
898
|
-
guardrails: {
|
|
899
|
-
mode: "throw",
|
|
900
|
-
budgets: {
|
|
901
|
-
maxUpdateTime: 5,
|
|
902
|
-
maxRecomputations: 50
|
|
903
|
-
},
|
|
904
|
-
customRules: [
|
|
905
|
-
rules.noFunctionsInState(),
|
|
906
|
-
rules.noDeepNesting(4)
|
|
907
|
-
],
|
|
908
|
-
...overrides
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
__name(createTestTree, "createTestTree");
|
|
913
|
-
|
|
914
|
-
exports.createAngularFeatureTree = createAngularFeatureTree;
|
|
915
|
-
exports.createAppShellTree = createAppShellTree;
|
|
916
|
-
exports.createCacheTree = createCacheTree;
|
|
917
|
-
exports.createFeatureTree = createFeatureTree;
|
|
918
|
-
exports.createFormTree = createFormTree;
|
|
919
|
-
exports.createPerformanceTree = createPerformanceTree;
|
|
920
|
-
exports.createTestTree = createTestTree;
|
|
921
|
-
//# sourceMappingURL=index.cjs.map
|
|
922
|
-
//# sourceMappingURL=index.cjs.map
|