@lensmcp/valtio-instrumentation 1.16.31 → 1.17.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/lib/snapshot.d.ts CHANGED
@@ -14,4 +14,6 @@ export declare function useTrackedSnapshot<T extends object>(target: T, opts: Us
14
14
  * `proxy()`; falls back to a single root subscription when nothing is read.
15
15
  */
16
16
  export declare function useSnapshot<T extends object>(target: T): T;
17
+ /** Join a visited-property path, truncated to {@link SUB_PATH_DEPTH} segments (0 ⇒ no truncation). */
18
+ export declare function truncatePath(parts: readonly (string | symbol)[]): string;
17
19
  //# sourceMappingURL=snapshot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot.ts"],"names":[],"mappings":"AAoCA,MAAM,WAAW,kBAAkB;IACjC;0DACsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;2DACuD;IACvD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,kBAAkB,GACvB,CAAC,CAcH;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAgF1D"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot.ts"],"names":[],"mappings":"AAoCA,MAAM,WAAW,kBAAkB;IACjC;0DACsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;2DACuD;IACvD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,kBAAkB,GACvB,CAAC,CAcH;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAgF1D;AA2BD,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,MAAM,CAGxE"}
package/lib/snapshot.js CHANGED
@@ -79,7 +79,7 @@ export function useSnapshot(target) {
79
79
  // Runs after every commit — `affected` now reflects this render's reads.
80
80
  useEffect(() => {
81
81
  const paths = affected
82
- ? affectedToPathList(currSnapshot, affected, true).map((parts) => parts.map(String).join('.'))
82
+ ? affectedToPathList(currSnapshot, affected, true).map((parts) => truncatePath(parts))
83
83
  : [];
84
84
  const next = new Set(paths.length ? paths : ['']);
85
85
  reconcileSubscriptions(subscribedRef.current, next, storeId, componentInstanceId);
@@ -98,6 +98,35 @@ export function useSnapshot(target) {
98
98
  ? createProxy(currSnapshot, affected, proxyCache, targetCache)
99
99
  : currSnapshot;
100
100
  }
101
+ /**
102
+ * Depth a tracked read path is reported at. `affectedToPathList` returns EVERY property visited, so a
103
+ * component that reads one large nested object through `useSnapshot` yields thousands of paths — and one
104
+ * event was published per path, per reconcile.
105
+ *
106
+ * Worked failure (foodguard, 2026-07-28): a run-timeline phase toggle re-rendered a list reading
107
+ * `runs[i].input` (a deep run payload). React rendered only **22** times, yet the bus took **26,216**
108
+ * `subscription-added/removed` events for a single store in 10s (peak 10,486/s, ~1,200 per render), and
109
+ * valtio events were **68%** of the whole event log — the mechanism behind the 60-84 MB `events.jsonl`
110
+ * files and an earlier OOM flood. It read as an app render storm and was not: the storm was the
111
+ * instrumentation.
112
+ *
113
+ * Truncating to the first N segments fixes it at the source instead of capping the emission: the `Set`
114
+ * then DEDUPES thousands of deep paths down to a handful, so the reducer's `storeId::path → subscribers`
115
+ * view stays complete and correct — just aggregated at a granularity a human can use
116
+ * (`entries.["runs","all"].data` rather than `…data.3.input.steps.7.config.foo`, which no one needs a
117
+ * subscriber list for). It also makes the set STABLE across renders, so churn stops generating
118
+ * remove+add pairs. Override with `LENSMCP_VALTIO_PATH_DEPTH`; `0` restores full untruncated paths.
119
+ */
120
+ const SUB_PATH_DEPTH = (() => {
121
+ const raw = typeof process !== 'undefined' ? process.env?.['LENSMCP_VALTIO_PATH_DEPTH'] : undefined;
122
+ const n = Number(raw);
123
+ return Number.isFinite(n) && n >= 0 && n <= 20 ? Math.floor(n) : 3;
124
+ })();
125
+ /** Join a visited-property path, truncated to {@link SUB_PATH_DEPTH} segments (0 ⇒ no truncation). */
126
+ export function truncatePath(parts) {
127
+ const segs = parts.map(String);
128
+ return (SUB_PATH_DEPTH > 0 ? segs.slice(0, SUB_PATH_DEPTH) : segs).join('.');
129
+ }
101
130
  /** Add/remove subscriptions so the registry matches `next` exactly. */
102
131
  function reconcileSubscriptions(prev, next, storeId, componentInstanceId) {
103
132
  for (const path of next) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/valtio-instrumentation",
3
- "version": "1.16.31",
3
+ "version": "1.17.0",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -14,7 +14,7 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@lensmcp/react-instrumentation": "1.16.31",
17
+ "@lensmcp/react-instrumentation": "1.17.0",
18
18
  "proxy-compare": "^3.0.0",
19
19
  "tslib": "^2.3.0"
20
20
  },