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