@buoy-gg/memory-leaks 0.0.0-stage → 7.0.80

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.
Files changed (47) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +14 -2
  3. package/lib/commonjs/components/MemoryLeaksBody.js +1 -0
  4. package/lib/commonjs/components/MemoryLeaksToolView.js +1 -0
  5. package/lib/commonjs/engine/capture.js +1 -0
  6. package/lib/commonjs/engine/capture.web.js +1 -0
  7. package/lib/commonjs/engine/runtime.js +1 -0
  8. package/lib/commonjs/engine/source.js +1 -0
  9. package/lib/commonjs/engine/store.js +1 -0
  10. package/lib/commonjs/engine/store.web.js +1 -0
  11. package/lib/commonjs/engine/tracker.js +1 -0
  12. package/lib/commonjs/index.js +1 -0
  13. package/lib/commonjs/package.json +1 -0
  14. package/lib/commonjs/preset.js +1 -0
  15. package/lib/commonjs/sync/memoryLeaksSyncAdapter.js +1 -0
  16. package/lib/commonjs/types.js +1 -0
  17. package/lib/commonjs/view.js +1 -0
  18. package/lib/module/components/MemoryLeaksBody.js +1 -0
  19. package/lib/module/components/MemoryLeaksToolView.js +1 -0
  20. package/lib/module/engine/capture.js +1 -0
  21. package/lib/module/engine/capture.web.js +1 -0
  22. package/lib/module/engine/runtime.js +1 -0
  23. package/lib/module/engine/source.js +1 -0
  24. package/lib/module/engine/store.js +1 -0
  25. package/lib/module/engine/store.web.js +1 -0
  26. package/lib/module/engine/tracker.js +1 -0
  27. package/lib/module/index.js +1 -0
  28. package/lib/module/preset.js +1 -0
  29. package/lib/module/sync/memoryLeaksSyncAdapter.js +1 -0
  30. package/lib/module/types.js +1 -0
  31. package/lib/module/view.js +1 -0
  32. package/lib/typescript/components/MemoryLeaksBody.d.ts +3 -0
  33. package/lib/typescript/components/MemoryLeaksToolView.d.ts +9 -0
  34. package/lib/typescript/engine/capture.d.ts +8 -0
  35. package/lib/typescript/engine/capture.web.d.ts +3 -0
  36. package/lib/typescript/engine/runtime.d.ts +11 -0
  37. package/lib/typescript/engine/source.d.ts +14 -0
  38. package/lib/typescript/engine/store.d.ts +17 -0
  39. package/lib/typescript/engine/store.web.d.ts +12 -0
  40. package/lib/typescript/engine/tracker.d.ts +55 -0
  41. package/lib/typescript/index.d.ts +10 -0
  42. package/lib/typescript/preset.d.ts +34 -0
  43. package/lib/typescript/sync/memoryLeaksSyncAdapter.d.ts +10 -0
  44. package/lib/typescript/types.d.ts +54 -0
  45. package/lib/typescript/view.d.ts +5 -0
  46. package/lib/web/index.mjs +1 -0
  47. package/package.json +96 -3
package/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ PROPRIETARY SOFTWARE LICENSE
2
+
3
+ Copyright (c) 2024-present Buoy. All rights reserved.
4
+
5
+ This software and its source code are proprietary and confidential.
6
+
7
+ NOTICE: This is NOT open source software. This software is licensed,
8
+ not sold, and is protected by copyright laws and international treaties.
9
+
10
+ TERMS AND CONDITIONS:
11
+
12
+ 1. LICENSE GRANT
13
+ Subject to the terms of this Agreement and payment of applicable fees,
14
+ Buoy grants you a limited, non-exclusive, non-transferable license
15
+ to use the compiled software packages in your applications.
16
+
17
+ 2. RESTRICTIONS
18
+ You may NOT:
19
+ - Copy, modify, or distribute the source code
20
+ - Reverse engineer, decompile, or disassemble the software
21
+ - Remove or alter any proprietary notices or labels
22
+ - Sublicense, rent, lease, or lend the software
23
+ - Use the software to create competing products
24
+ - Share access credentials with unauthorized parties
25
+
26
+ 3. OWNERSHIP
27
+ React Buoy retains all right, title, and interest in the software,
28
+ including all intellectual property rights. This license does not
29
+ grant you any rights to trademarks or service marks.
30
+
31
+ 4. TERMINATION
32
+ This license is effective until terminated. Your rights under this
33
+ license will terminate automatically without notice if you fail to
34
+ comply with any of its terms. Upon termination, you must cease all
35
+ use and destroy all copies of the software.
36
+
37
+ 5. DISCLAIMER OF WARRANTIES
38
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
41
+
42
+ 6. LIMITATION OF LIABILITY
43
+ IN NO EVENT SHALL BUOY BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
44
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN
45
+ CONNECTION WITH THIS LICENSE OR THE USE OF THE SOFTWARE.
46
+
47
+ 7. GOVERNING LAW
48
+ This Agreement shall be governed by and construed in accordance with
49
+ the laws of the United States, without regard to its conflict of
50
+ law provisions.
51
+
52
+ For licensing inquiries and subscription information:
53
+ - Website: https://buoy.gg
54
+ - Email: AustinLovesWorking@gmail.com
55
+
56
+ Unauthorized reproduction or distribution of this software, or any
57
+ portion of it, may result in severe civil and criminal penalties,
58
+ and will be prosecuted to the maximum extent possible under the law.
package/README.md CHANGED
@@ -1,3 +1,15 @@
1
- # Temporary Holding Version
1
+ # @buoy-gg/memory-leaks
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Check what stays alive when a screen leaves. See suspects and byte growth per loop. A suspect is a clue. This tool cannot prove every leak.
4
+
5
+ ```bash
6
+ pnpm add @buoy-gg/memory-leaks
7
+ ```
8
+
9
+ The tool shows up in the Buoy dial. Reads and checks need Pro and a native dev build. Release imports are inert. Desktop shows the same report from the linked app.
10
+
11
+ Add `@buoy-gg/clock` for its shared timer feed. Add `@buoy-gg/route-events` and mount `RouteTracker` for route loops. Both packages are optional. Missing sources show a reason. Missing byte reads stay `null`.
12
+
13
+ Class instances are watched on unmount. Function checks watch props and object hook values. This includes state, refs and memo values. They use weak refs from the React unmount hook. Loop checks need growth across three loops. The owner path and route help place each hit. Use `useMemoryLeakWatch(label, target)` for a stable object in a function screen. This explicit hook works for your own stable targets. App timer and listener rows show the source file and line when Metro can map them. An empty report without GC proof does not prove clean.
14
+
15
+ [Setup, API and limits](https://buoy.gg/buoy/latest/docs/tools/memory-leaks)
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.MemoryLeaksBody=MemoryLeaksBody;var _react=_interopRequireWildcard(require("react"));var _store=require("../engine/store");var _memoryLeaksSyncAdapter=require("../sync/memoryLeaksSyncAdapter");var _MemoryLeaksToolView=require("./MemoryLeaksToolView");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(a,s){if("function"==typeof WeakMap)var i=new WeakMap,u=new WeakMap;return(_interopRequireWildcard=function(e,c){if(!c&&e&&e.__esModule)return e;var r,n,o={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return o;if(r=c?u:i){if(r.has(e))return r.get(e);r.set(e,o)}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((n=(r=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(n.get||n.set)?r(o,t,n):o[t]=e[t]);return o})(a,s)}function MemoryLeaksBody(){const[a,s]=(0,_react.useState)(_store.getMemoryReport);(0,_react.useEffect)(()=>(0,_store.subscribeMemoryLeaks)(()=>s((0,_store.getMemoryReport)())),[]);return(0,_jsxRuntime.jsx)(_MemoryLeaksToolView.MemoryLeaksToolView,{snapshot:a,onAction:async(i,u)=>{const e=_memoryLeaksSyncAdapter.memoryLeaksSyncAdapter.actions[i];if(!e)throw new Error("That action is not here.");return e(u)}})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.MemoryLeaksToolView=MemoryLeaksToolView;exports.formatGrowth=formatGrowth;var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(e,c){if("function"==typeof WeakMap)var g=new WeakMap,m=new WeakMap;return(_interopRequireWildcard=function(r,x){if(!x&&r&&r.__esModule)return r;var n,d,a={__proto__:null,default:r};if(null===r||"object"!=typeof r&&"function"!=typeof r)return a;if(n=x?m:g){if(n.has(r))return n.get(r);n.set(r,a)}for(const s in r)"default"!==s&&{}.hasOwnProperty.call(r,s)&&((d=(n=Object.defineProperty)&&Object.getOwnPropertyDescriptor(r,s))&&(d.get||d.set)?n(a,s,d):a[s]=r[s]);return a})(e,c)}function formatGrowth(e){if(e===null)return"No byte read";const c=Math.abs(e)>=1048576?"MB":"KB";return`${e>0?"+":""}${(e/(c==="MB"?1048576:1024)).toFixed(1)} ${c}`}const kinds=[["component","Components"],["interval","Intervals"],["timeout","Timeouts"],["listener","Listeners"],["subscription","Subscriptions"]];function MemoryLeaksToolView({snapshot:e,onAction:c}){const[g,m]=(0,_react.useState)("/");const[r,x]=(0,_react.useState)("");const[n,d]=(0,_react.useState)("5");const[a,s]=(0,_react.useState)("");const[b,f]=(0,_react.useState)(false);const[j,T]=(0,_react.useState)(null);const[v,w]=(0,_react.useState)(false);const S=async(o,i)=>{s("");f(true);try{await c(o,i)}catch(l){s(l instanceof Error?l.message:String(l))}finally{f(false)}};const u=(o,i,l,t=false)=>(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{label:o,testID:`memory-${i}`,disabled:b,variant:t?"primary":"secondary",onPress:()=>void S(i,l)});const p=e?.suspects.reduce((o,i)=>o+i.liveCount,0)??0;const h=e?.status==="running";const y=e?.status==="complete";const C=h?"Check in progress":p?`${p} suspects found`:y?"No suspects above the limit":"Watch what stays alive";return(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{keyboardShouldPersistTaps:"handled",testID:"memory-leaks-view",style:styles.root,contentContainerStyle:styles.content,children:[!e?(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{centered:true,children:"Pick a linked device with Memory Leaks installed."}):(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsxs)(_sharedUi.NightCard,{padded:true,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.spread,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.eyebrow,children:"SCREEN EXIT CHECK"}),(0,_jsxRuntime.jsx)(_sharedUi.NightBadge,{label:e.status,tone:h?"info":p?"warning":y?"accent":"neutral"})]}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.title,children:C}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{testID:"memory-status",style:styles.caption,children:[e.status," \xB7 ",e.cyclesDone," loops"]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.stats,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.stat,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.caption,children:"JS / loop"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:"memory-growth",style:styles.value,children:formatGrowth(e.growthPerCycle.jsBytes)})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.stat,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.caption,children:"Native / loop"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:"memory-native-growth",style:styles.value,children:formatGrowth(e.growthPerCycle.nativeBytes)})]})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.spread,children:[(0,_jsxRuntime.jsx)(_sharedUi.NightBadge,{label:e.gcProved?"GC proved":"GC not proved",tone:e.gcProved?"accent":"warning"}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.caption,children:[e.pendingGcCount," await GC"]})]})]}),!e.gcProved&&e.status!=="idle"&&(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{tone:"warning",children:"A blank list does not prove this screen is clean."}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.actions,children:h?(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[e.mode==="watch"&&u("Finish watch","finishWatch",void 0,true),u("Stop check","stopCheck",{runId:e.runId})]}):(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{label:"Run check",testID:"memory-run-setup",variant:"primary",onPress:()=>w(!v)}),u("Watch exit","startWatch")]})}),v&&!h&&(0,_jsxRuntime.jsxs)(_sharedUi.NightCard,{padded:true,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.label,children:"Home route"}),(0,_jsxRuntime.jsx)(_reactNative.TextInput,{accessibilityLabel:"Home route",testID:"memory-baseline",style:styles.input,value:g,onChangeText:m,autoCapitalize:"none"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.label,children:"Test route"}),(0,_jsxRuntime.jsx)(_reactNative.TextInput,{accessibilityLabel:"Test route",testID:"memory-target",style:styles.input,value:r,onChangeText:x,autoCapitalize:"none",placeholder:"/profile",placeholderTextColor:_sharedUi.night.color.placeholder}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.label,children:"Loops (1 to 20)"}),(0,_jsxRuntime.jsx)(_reactNative.TextInput,{accessibilityLabel:"Loops",style:styles.input,value:n,onChangeText:d,keyboardType:"numeric"}),u("Start loops","startCheck",{baselinePath:g,targetPath:r,cycles:Number(n)},true)]}),kinds.map(([o,i])=>{const l=e.suspects.filter(t=>t.kind===o);if(!l.length)return null;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.group,children:[(0,_jsxRuntime.jsx)(_sharedUi.NightSectionLabel,{children:`${i} \xB7 ${l.reduce((t,k)=>t+k.liveCount,0)}`}),l.map(t=>(0,_jsxRuntime.jsxs)(_sharedUi.NightCard,{padded:true,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.spread,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:`memory-suspect-${o}`,style:styles.rowTitle,children:t.owner??t.appSource?.split(" \xB7 ")[0]??t.source}),(0,_jsxRuntime.jsx)(_sharedUi.NightBadge,{label:`${t.liveCount} live`,tone:"warning"})]}),(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:`memory-source-${o}`,selectable:true,style:styles.source,children:t.appSource??(t.kind==="component"?t.ownerPath??t.source:"App source not found. Open the add stack.")}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.caption,children:[t.route??"Route unknown"," \xB7 ",t.evidence==="survived-gc"?"Still alive after GC":t.evidence==="repeated-growth"?"Grows with each loop":"Still live at this exit"]}),t.stack&&(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{size:"md",variant:"ghost",label:j===t.id?"Hide stack":"Show add stack",onPress:()=>T(j===t.id?null:t.id)}),j===t.id&&(0,_jsxRuntime.jsx)(_reactNative.Text,{selectable:true,style:styles.caption,children:t.stack})]},t.id))]},o)}),!p&&(0,_jsxRuntime.jsxs)(_sharedUi.NightCard,{padded:true,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,children:y?"No new suspects":"Leave a screen to check it"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.caption,children:y?"Try more loops or watch a known leak.":"Run route loops, or watch one exit by hand."})]}),(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{children:"Suspects are clues. This cannot prove every leak."}),(0,_jsxRuntime.jsxs)(_sharedUi.NightFootnote,{children:[e.belowThresholdCount," below the limit \xB7 ",e.droppedCount," dropped"]}),Object.entries(e.capabilities).filter(([,o])=>!o.available).map(([o,i])=>(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{children:i.reason},o)),e.notes.map((o,i)=>(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{children:o},i)),!h&&e.status!=="idle"&&u("Clear report","clearReports")]}),!!a&&(0,_jsxRuntime.jsx)(_reactNative.Text,{accessibilityRole:"alert",style:styles.error,children:a})]})}const styles=_reactNative.StyleSheet.create({root:{flex:1},content:{padding:_sharedUi.night.gutter,paddingBottom:60,gap:12},spread:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8},eyebrow:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.micro,letterSpacing:.8},title:{color:_sharedUi.night.color.text,fontSize:21,fontWeight:"600",marginTop:14,marginBottom:5},caption:{color:_sharedUi.night.color.textSecondary,fontSize:12,lineHeight:18},stats:{flexDirection:"row",gap:10,marginVertical:16},stat:{flex:1,padding:12,borderRadius:_sharedUi.night.radius.row,backgroundColor:_sharedUi.night.color.surfaceElevated},value:{color:_sharedUi.night.color.text,fontSize:21,fontWeight:"600",marginTop:5},actions:{gap:8},group:{gap:8,marginTop:6},rowTitle:{color:_sharedUi.night.color.text,fontSize:14,fontWeight:"600",flexShrink:1},source:{color:_sharedUi.night.color.accent,fontSize:12,lineHeight:18,marginVertical:8},label:{color:_sharedUi.night.color.textSecondary,fontSize:13,marginVertical:8},input:{color:_sharedUi.night.color.text,backgroundColor:_sharedUi.night.color.surfaceElevated,borderRadius:_sharedUi.night.radius.input,padding:12,marginBottom:10},error:{color:_sharedUi.night.color.danger}});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.capture=capture;exports.watchObject=watchObject;var _reactNative=require("react-native");var _runtime=require("./runtime");const g=globalThis;let active=null;let installedHook=null;let weakTargets=new WeakSet;let seen=new WeakMap;function watchObject(n,u){const s=active;const d=s?.add("component",n,null,u,n,"explicit")??null;return()=>s?.exit(d)}function ownValue(n,u){try{return Object.getOwnPropertyDescriptor(n,u)?.value}catch{return void 0}}function installHook(){const n=g.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!n)return false;if(installedHook===n)return true;const u=n.onCommitFiberRoot;const s=n.onCommitFiberUnmount;const d=function(...f){try{u?.apply(this,f)}finally{try{const c=active;if(c?.capturing){const a=[f[1]?.current];let w=0;while(a.length&&w++<1e4){const e=a.pop();if(!e)continue;if(e.sibling)a.push(e.sibling);if(e.child)a.push(e.child);if(e.tag!==1||!e.stateNode||seen.has(e.stateNode))continue;const i=e.type?.displayName||e.type?.name||"Class";const t=c.add("component",i,null,e.stateNode,i,"instance");seen.set(e.stateNode,t)}}}catch{}}};const h=function(...f){try{s?.apply(this,f)}finally{try{const c=active;if(c?.capturing){const a=f[1];const w=a?.stateNode;if(w)c.exit(seen.get(w)??null);if(a&&[0,11,15].includes(a.tag)){const e=[];const i=r=>{if(r&&typeof r==="object"&&!weakTargets.has(r)){weakTargets.add(r);e.push(r)}};i(a.memoizedProps);let t=a.memoizedState;for(let r=0;t&&r<64;r++,t=t.next){const o=t.memoizedState;if(!o||typeof o!=="object")continue;if(typeof t.queue?.dispatch==="function"){i(o);continue}if("create"in o&&"next"in o||"pending"in o&&"dispatch"in o||"impl"in o&&typeof ownValue(o,"impl")==="function"||typeof ownValue(o,"then")==="function")continue;if(Array.isArray(o)){if(o.length===2&&(o[1]===null||Array.isArray(o[1])))i(o[0])}else{i(o);if(Object.prototype.hasOwnProperty.call(o,"current"))i(ownValue(o,"current"))}}const y=r=>r?.type?.displayName||r?.type?.name||r?.type?.render?.name;const v=y(a)||"Function";const E=[v];let m=a._debugOwner??a.return;for(let r=0;m&&r<12;r++,m=m._debugOwner??m.return){const o=y(m);if(o)E.unshift(o)}c.addFiber(v,e,E.join(" > "))}}}catch{}}};try{n.onCommitFiberRoot=d;n.onCommitFiberUnmount=h;if(n.onCommitFiberRoot!==d||n.onCommitFiberUnmount!==h)throw new Error("sealed")}catch{try{if(n.onCommitFiberRoot===d)n.onCommitFiberRoot=u}catch{}try{if(n.onCommitFiberUnmount===h)n.onCommitFiberUnmount=s}catch{}return false}installedHook=n;return true}function safeInstallHook(){try{return installHook()}catch{return false}}function capture(n){active=n;seen=new WeakMap;weakTargets=new WeakSet;const u=[];const s={weakRef:(0,_runtime.support)(typeof g.WeakRef==="function","This VM has no WeakRef."),finalizationRegistry:(0,_runtime.support)(typeof g.FinalizationRegistry==="function","This VM has no finalizers. Weak refs still work."),reactUnmountHook:(0,_runtime.support)(safeInstallHook(),"The React hook is missing. Use useMemoryLeakWatch."),gc:(0,_runtime.support)(typeof g.gc==="function","No GC call. Suspects need three loops and time.")};let d=0;let h=null;let f=new WeakMap;const c=new Set;const a=e=>{let i=f.get(e);if(!i){i=new Map;f.set(e,i)}c.add(i);return i};const w=(e,i,t,y=false)=>{const v=e?.[i];if(typeof v!=="function"){s[t]=(0,_runtime.support)(false,"This add hook is not here.");return}let E=true;function m(...r){if(!E)return v.apply(this,r);const o=t==="NativeEventEmitter"||t==="DeviceEventEmitter";if(d){if(o)h={receiver:_reactNative.DeviceEventEmitter,event:String(r[0])};return v.apply(this,r)}h=null;d++;let l;try{l=v.apply(this,r)}finally{d--}const k=y?"change":String(r[0]);const p=n.add(y?"subscription":"listener",`${t}.${k}`,new Error().stack??null);if(!p)return l;const N=h;h=null;const C=N?.event??k;const b=a(N?.receiver??(o?_reactNative.DeviceEventEmitter:this));const O=b.get(C)??new Set;O.add(p);b.set(C,O);const F=typeof l==="function"?l:l?.remove;const A=()=>{n.remove(p);O.delete(p);if(!O.size)b.delete(C);if(!b.size)c.delete(b)};if(typeof F!=="function"){A();s[t]=(0,_runtime.support)(false,"The add hook has no remove handle.");return l}if(typeof l==="function")return function(...L){const T=F.apply(this,L);A();return T};try{l.remove=function(...L){const T=F.apply(this,L);A();return T}}catch{A();s[t]=(0,_runtime.support)(false,"The remove hook is sealed.")}return l}try{e[i]=m;if(e[i]!==m)throw new Error("sealed");s[t]=(0,_runtime.support)(true,"");u.push(()=>{E=false;f=new WeakMap;if(e[i]===m)e[i]=v});const r=e.removeAllListeners;if(typeof r==="function"){let o=function(l){const k=r.apply(this,arguments);if(!E)return k;const p=f.get(t==="NativeEventEmitter"||t==="DeviceEventEmitter"?_reactNative.DeviceEventEmitter:this);if(!p)return k;for(const[N,C]of p)if(l===void 0||N===l){for(const b of C)n.remove(b);C.clear();p.delete(N);if(!p.size)c.delete(p)}return k};e.removeAllListeners=o;u.push(()=>{if(e.removeAllListeners===o)e.removeAllListeners=r})}}catch{s[t]=(0,_runtime.support)(false,"This hook is sealed.")}};for(const[e,i]of[[_reactNative.AppState,"AppState"],[_reactNative.Dimensions,"Dimensions"],[_reactNative.Keyboard,"Keyboard"],[_reactNative.Linking,"Linking"],[_reactNative.DeviceEventEmitter,"DeviceEventEmitter"],[_reactNative.NativeEventEmitter?.prototype,"NativeEventEmitter"]]){try{w(e,"addEventListener"in(e??{})?"addEventListener":"addListener",i)}catch{s[i]=(0,_runtime.support)(false,"Could not read this add hook.")}}try{w(require("@react-native-community/netinfo").default,"addEventListener","NetInfo",true)}catch{s.NetInfo=(0,_runtime.support)(false,"NetInfo is not installed.")}try{const e=new Map;const i=require("@buoy-gg/clock/timers").observeTimers(t=>{if(t.event==="add"){const y=n.add(t.kind,`Clock.${t.kind}`,t.stack??null,void 0,null,"unknown",t.delay);if(y)e.set(t.id,y)}else if(t.event!=="fire"||t.kind==="timeout"){n.remove(e.get(t.id)??null);e.delete(t.id)}});u.push(i);s.timers=(0,_runtime.support)(true,"")}catch{s.timers=(0,_runtime.support)(false,"Timers are not tracked. Install @buoy-gg/clock for its shared timer feed.")}return{sources:s,stop(){if(active===n)active=null;n.capturing=false;for(const e of u.splice(0).reverse()){try{e()}catch{}}for(const e of c){for(const i of e.values())i.clear();e.clear()}c.clear();n.clear()}}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.watchObject=watchObject;function watchObject(e,t){return()=>{}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.collect=collect;exports.now=void 0;exports.sample=sample;exports.sleep=void 0;exports.stats=stats;exports.support=support;const g=globalThis;const now=()=>g.__buoyRealNow?.()??Date.now();exports.now=now;const sleep=t=>new Promise(e=>(g.__buoyRealTimers??g).setTimeout(e,t));exports.sleep=sleep;function stats(){try{return g.HermesInternal?.getInstrumentedStats?.()??{}}catch{return{}}}function support(t,e){return{available:t,reason:t?null:e}}function sample(t,e){const s=stats();let n=null;try{const r=require("@buoy-gg/perf-monitor");if(r.probeCapabilities().accurateMemory){const o=r.loadToolkit()?.getMemoryUsage();if(typeof o==="number"&&Number.isFinite(o)&&o>0)n=o*1024*1024}}catch{}return{cycle:t,at:now(),exitObserved:e,jsBytes:Number.isFinite(s.js_allocatedBytes)?s.js_allocatedBytes:null,gcCount:Number.isFinite(s.js_numGCs)?s.js_numGCs:null,nativeBytes:n}}async function collect(){await sleep(50);const t=stats().js_numGCs;try{if(typeof g.gc==="function")g.gc()}catch{}await sleep(50);const e=stats().js_numGCs;return{count:Number.isFinite(e)?e:null,proved:Number.isFinite(t)&&Number.isFinite(e)&&e>t}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.appFrame=appFrame;exports.publicStack=publicStack;exports.resolveSources=resolveSources;function publicStack(l){return l?.replace(/https?:\/\/[^)\s]+:(\d+):(\d+)/g,"bundle:$1:$2").replace(/https?:\/\/[^)\s]+/g,"bundle")??null}function appFrame(l){const t=l.find(r=>r.file&&r.lineNumber&&!r.collapse&&!/node_modules|\/packages\/(?:clock|js-top|memory-leaks|shared|devtools-floating-menu)\/|@buoy-gg|\/Libraries\//.test(r.file)&&!/^https?:|\.bundle(?:\?|$)/.test(r.file));if(!t)return null;const o=t.file.replace(/^.*\/(?:example-dev-build|example|src)\//,"");return`${(t.methodName||"App").slice(0,200)} \xB7 ${o.slice(-500)}:${t.lineNumber}`}async function resolveSources(l){const t=[...new Set(l.map(e=>e.stack).filter(e=>!!e))];const o=new Map;let r;try{const e=require("react-native/Libraries/Core/Devtools/parseErrorStack").default;const c=require("react-native/Libraries/Core/Devtools/getDevServer").default();if(!c.bundleLoadedFromServer||!t.length)throw new Error("No source map.");const i=t.map(s=>e(s).slice(0,8));const u=new AbortController;const p=globalThis.__buoyRealTimers??globalThis;const m=new Promise((s,a)=>{r=p.setTimeout(()=>{u.abort();a(new Error("Source map timed out."))},3e3)});const d=fetch(`${c.url}symbolicate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({stack:i.flat()}),signal:u.signal}).then(s=>s.json());const b=await Promise.race([d,m]);let n=0;for(let s=0;s<t.length;s++){const a=i[s].length;o.set(t[s],appFrame(b.stack.slice(n,n+a)));n+=a}}catch{}finally{(globalThis.__buoyRealTimers??globalThis).clearTimeout(r)}return l.map(e=>({...e,appSource:e.stack?o.get(e.stack)??null:null,stack:publicStack(e.stack)}))}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.clearMemoryReports=clearMemoryReports;exports.finishMemoryWatch=finishMemoryWatch;exports.getMemoryReport=getMemoryReport;exports.readMemoryReport=readMemoryReport;exports.startMemoryCheck=startMemoryCheck;exports.startMemoryWatch=startMemoryWatch;exports.stopMemoryCheck=stopMemoryCheck;exports.subscribeMemoryLeaks=subscribeMemoryLeaks;exports.validateInput=validateInput;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _tracker=require("./tracker");var _source=require("./source");var _capture=require("./capture");var _runtime=require("./runtime");const listeners=new Set;const g=globalThis;let sequence=0;let report=empty();let run=null;function empty(){return{runId:"",status:"idle",mode:"loop",cyclesDone:0,capabilities:{},samples:[],growthPerCycle:{jsBytes:null,nativeBytes:null},suspects:[],pendingGcCount:0,belowThresholdCount:0,collectedCount:0,gcProved:false,droppedCount:0,notes:[]}}function allowed(){return typeof __DEV__!=="undefined"&&__DEV__&&_reactNative.Platform.OS!=="web"&&(0,_sharedUi.isProNow)()}function gate(){if(!allowed())throw new Error("Memory checks need Pro and a native dev build.")}function emit(){report={...report};for(const r of listeners){try{r()}catch{}}}function subscribeMemoryLeaks(r){listeners.add(r);return()=>{listeners.delete(r)}}function getMemoryReport(r){if(!allowed())return{...empty(),notes:["Memory checks need Pro and a native dev build."]};const e=r?.runId;if(e&&e!==report.runId)throw new Error("That run is no longer here.");return{...report,suspects:report.suspects.map(a=>({...a,stack:(0,_source.publicStack)(a.stack)}))}}function readMemoryReport(r){gate();return getMemoryReport(r)}function validateInput(r){const e=r;const a=e?.cycles??5,t=e?.settleMs??1500;for(const s of[e?.baselinePath,e?.targetPath])if(typeof s!=="string"||!s.startsWith("/")||s.length>300||/[?#]/.test(s))throw new Error("Use a route path with no query or hash.");if(e.baselinePath===e.targetPath)throw new Error("Pick two distinct routes.");if(!Number.isInteger(a)||a<1||a>20)throw new Error("Use 1 to 20 loops.");if(!Number.isInteger(t)||t<500||t>5e3)throw new Error("Use a wait from 500 to 5000 ms.");return{baselinePath:e.baselinePath,targetPath:e.targetPath,cycles:a,settleMs:t}}function begin(r){gate();if(run)throw new Error("A check is still running.");const e=new _tracker.Tracker(_runtime.now,typeof g.WeakRef==="function"?s=>new g.WeakRef(s):void 0,5e3,r==="loop"?3:1);report={...empty(),runId:`${(0,_runtime.now)()}-${++sequence}`,status:"running",mode:r};const a=[];const t={tracker:e,cancelled:false,finishing:false,deadline:(0,_runtime.now)()+18e4,stopCapture(){for(const s of a.splice(0).reverse()){try{s()}catch{}}}};run=t;try{const s=_reactNative.AppState.addEventListener("change",c=>{if(run===t&&c!=="active")end("partial","The app left the screen.")});a.push(()=>s.remove());a.push((0,_sharedUi.subscribeAccountAccess)(()=>{if(run===t&&!allowed())end("partial","Pro access ended.")}));assertRun(t);const n=(0,_capture.capture)(e);a.push(n.stop);const o=g.__buoyRealTimers??g;const i=o.setTimeout(()=>{if(run===t)end("partial","The check hit its three minute limit.")},18e4);a.push(()=>o.clearTimeout(i));report.capabilities=n.sources;emit();return t}catch(s){t.stopCapture();if(run===t)end("partial","Capture could not start.");throw s}}function end(r,e){const a=run;if(!a)return;a.cancelled=true;run=null;a.stopCapture();report.status=r;if(e)report.notes=[...report.notes,e];if(!report.gcProved)report.notes=[...report.notes,"GC was not proved. A blank list does not prove this screen is clean."];emit()}function assertRun(r){if(r.cancelled||run!==r)throw new Error("Check stopped.");gate();if((0,_runtime.now)()>r.deadline)throw new Error("The check hit its three minute limit.")}async function pause(r,e){const a=(0,_runtime.now)()+e;do{assertRun(r);await(0,_runtime.sleep)(Math.min(100,Math.max(0,a-(0,_runtime.now)())))}while((0,_runtime.now)()<a)}async function inspect(r,e,a){for(let n=0;n<2;n++){const o=await(0,_runtime.collect)();assertRun(r);report.gcProved||=o.proved;Object.assign(report,r.tracker.inspect(o.count,o.proved))}const t=(0,_runtime.sample)(e,a);report.samples=[...report.samples,t];report.cyclesDone=e;report.capabilities.jsHeap=(0,_runtime.support)(t.jsBytes!==null,"The VM has no live JS byte read.");report.capabilities.nativeMemory=(0,_runtime.support)(t.nativeBytes!==null,"Perf Monitor has no native byte read here.");const s=report.samples[0];for(const n of["jsBytes","nativeBytes"]){report.growthPerCycle[n]=e&&s[n]!==null&&t[n]!==null?(t[n]-s[n])/e:null}emit()}async function routeWait(r,e,a){const t=(0,_runtime.now)()+1e4;while((0,_runtime.now)()<t){assertRun(r);const s=await e.getCurrentRoute();const n=o=>o.replace(/\/\([^/]+\)/g,"").replace(/\/index$/,"").replace(/\/$/,"")||"/";if(!s?.stale&&typeof s?.path==="string"&&n(s.path)===n(a))return;await pause(r,100)}throw new Error(`Route did not reach ${a}. Check RouteTracker.`)}function startMemoryCheck(r){gate();const e=validateInput(r);let a;try{a=require("@buoy-gg/route-events").routeEventsSyncAdapter.actions}catch{throw new Error("Add @buoy-gg/route-events to run route loops.")}const t=begin("loop");void(async()=>{try{(0,_runtime.sample)(-1,false);await a.navigate({path:e.baselinePath,replace:true});await routeWait(t,a,e.baselinePath);await pause(t,500);t.tracker.route=e.targetPath;t.tracker.capturing=true;await a.navigate({path:e.targetPath});await routeWait(t,a,e.targetPath);await pause(t,e.settleMs);await a.stackGoBack();await routeWait(t,a,e.baselinePath);await pause(t,e.settleMs);t.tracker.capturing=false;t.tracker.exitCycle();await inspect(t,0,false);t.tracker.sealBaseline();report.suspects=[];report.pendingGcCount=0;for(let n=1;n<=e.cycles;n++){assertRun(t);t.tracker.cycle=n;t.tracker.route=e.targetPath;t.tracker.capturing=true;const o=t.tracker.exits;await a.navigate({path:e.targetPath});await routeWait(t,a,e.targetPath);await pause(t,e.settleMs);await a.stackGoBack();await routeWait(t,a,e.baselinePath);await pause(t,e.settleMs);t.tracker.capturing=false;if(t.tracker.exits===o)throw new Error("No watched object left. The screen may stay mounted. Use useMemoryLeakWatch in this screen.");t.tracker.exitCycle();await inspect(t,n,true)}const s=await(0,_source.resolveSources)(report.suspects);assertRun(t);report.suspects=s;end("complete")}catch(s){if(run===t)end("partial",s instanceof Error?s.message:String(s))}})();return{runId:report.runId,status:"running"}}function startMemoryWatch(){const r=begin("watch");r.tracker.capturing=true;r.tracker.cycle=1;report.samples=[(0,_runtime.sample)(0,false)];emit();return{runId:report.runId,status:"running"}}async function finishMemoryWatch(){gate();const r=run;if(!r||report.mode!=="watch")throw new Error("Start a watch first.");if(r.finishing)throw new Error("This watch is being checked.");r.finishing=true;try{r.tracker.capturing=false;await pause(r,1500);r.tracker.exitCycle();await inspect(r,1,r.tracker.exits>0);const e=await(0,_source.resolveSources)(report.suspects);assertRun(r);report.suspects=e;end(r.tracker.exits?"complete":"partial",r.tracker.exits?void 0:"No watched object left.");return readMemoryReport()}catch(e){if(run===r)end("partial",String(e));throw e}}function stopMemoryCheck(r){gate();const e=r?.runId;if(!e||e!==report.runId)throw new Error("Use the current runId to stop.");end("cancelled");return readMemoryReport()}function clearMemoryReports(){gate();if(run)throw new Error("Stop the check first.");report=empty();emit();return report}if(typeof module!=="undefined")module.hot?.dispose(()=>end("cancelled","JS reloaded."));
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.finishMemoryWatch=exports.clearMemoryReports=void 0;exports.getMemoryReport=getMemoryReport;exports.stopMemoryCheck=exports.startMemoryWatch=exports.startMemoryCheck=exports.readMemoryReport=void 0;exports.subscribeMemoryLeaks=subscribeMemoryLeaks;function getMemoryReport(){return{runId:"",status:"idle",mode:"loop",cyclesDone:0,capabilities:{},samples:[],growthPerCycle:{jsBytes:null,nativeBytes:null},suspects:[],pendingGcCount:0,belowThresholdCount:0,collectedCount:0,gcProved:false,droppedCount:0,notes:["Pick a native dev build to run checks."]}}function unavailable(){throw new Error("Run this check on a linked native dev build.")}const readMemoryReport=exports.readMemoryReport=unavailable;const startMemoryCheck=exports.startMemoryCheck=unavailable;const startMemoryWatch=exports.startMemoryWatch=unavailable;const finishMemoryWatch=exports.finishMemoryWatch=unavailable;const stopMemoryCheck=exports.stopMemoryCheck=unavailable;const clearMemoryReports=exports.clearMemoryReports=unavailable;function subscribeMemoryLeaks(e){return()=>{}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.Tracker=void 0;class Tracker{rows=new Map;seq=0;baseline=new Set;key(e){return JSON.stringify([e.kind,e.source,e.owner,e.ownerPath,e.stack])}sealBaseline(){for(const e of this.rows.keys())this.baseline.add(e)}dropped=0;collected=0;cycle=0;route=null;capturing=false;exits=0;constructor(e,i,o=5e3,n=1){this.now=e;this.makeRef=i;this.cap=o;this.minimumCycles=n}add(e,i,o=null,n,r=null,l="unknown",d){if(!this.capturing)return null;if(this.rows.size>=this.cap){this.dropped++;return null}if(n&&!this.makeRef)return null;const s=String(++this.seq);this.rows.set(s,{id:s,kind:e,source:i.slice(0,200),stack:o?.split("\n").slice(0,8).join("\n").slice(0,2e3)??null,owner:r?.slice(0,200)??null,ownerBasis:l,route:this.route?.slice(0,300)??null,at:this.now(),cycle:this.cycle,exited:false,...e==="timeout"&&d!==void 0?{dueAt:this.now()+d}:{},...n?{ref:this.makeRef(n)}:{},gcReads:0,lastGc:null});return s}addFiber(e,i,o){if(!i.length||!this.makeRef)return;const n=this.add("component",e,null,i[0],e,"fiber");const r=n?this.rows.get(n):void 0;if(r){r.refs=[r.ref,...i.slice(1).map(l=>this.makeRef(l))];r.ownerPath=o.slice(0,600);this.exit(n)}}remove(e){if(e)this.rows.delete(e)}exit(e){const i=e?this.rows.get(e):void 0;if(i&&!i.exited){i.exited=true;this.exits++}}exitCycle(){for(const e of this.rows.values())if(e.cycle===this.cycle&&!e.ref)e.exited=true}inspect(e,i){const o=new Map;let n=0;let r=0;for(const[d,s]of this.rows){if(!s.exited)continue;if(s.ref){if(!(s.refs??[s.ref]).some(h=>h.deref())){this.rows.delete(d);this.collected++;continue}if(i&&e!==null&&s.lastGc!==e){s.gcReads++;s.lastGc=e}}const t=this.key(s);const u=o.get(t)??{rows:[],cycles:new Set};u.rows.push(s);u.cycles.add(s.cycle);o.set(t,u)}const l=[];for(const{rows:d}of o.values()){const s=d.filter(c=>!this.baseline.has(c.id));if(!s.length)continue;const t=s[0];const u=this.now()-t.at;const h=new Set(s.map(c=>c.cycle));if((!t.ref||t.ownerBasis==="fiber")&&(h.size<this.minimumCycles||this.minimumCycles>1&&u<3e3||s.some(c=>c.dueAt!==void 0&&c.dueAt>this.now()))){r+=s.length;continue}const a=s.every(c=>!c.ref||c.gcReads>=2);const f=h.size>=3&&u>=3e3;if(t.ref&&!a&&!f){n+=s.length;continue}l.push({id:t.id,kind:t.kind,source:t.source,owner:t.owner,ownerBasis:t.ownerBasis,ownerPath:t.ownerPath,route:t.route,stack:t.stack,liveCount:s.length,growthCount:s.length,ageMs:u,evidence:t.ref&&a?"survived-gc":f?"repeated-growth":"still-live"})}return{suspects:l.slice(0,200),pendingGcCount:n,belowThresholdCount:r,collectedCount:this.collected,droppedCount:this.dropped+Math.max(0,l.length-200)}}clear(){this.rows.clear();this.baseline.clear()}}exports.Tracker=Tracker;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"MemoryLeaksBody",{enumerable:true,get:function(){return _MemoryLeaksBody.MemoryLeaksBody}});Object.defineProperty(exports,"MemoryLeaksIcon",{enumerable:true,get:function(){return _preset.MemoryLeaksIcon}});Object.defineProperty(exports,"MemoryLeaksToolView",{enumerable:true,get:function(){return _MemoryLeaksToolView.MemoryLeaksToolView}});Object.defineProperty(exports,"clearMemoryReports",{enumerable:true,get:function(){return _store.clearMemoryReports}});Object.defineProperty(exports,"createMemoryLeaksTool",{enumerable:true,get:function(){return _preset.createMemoryLeaksTool}});Object.defineProperty(exports,"finishMemoryWatch",{enumerable:true,get:function(){return _store.finishMemoryWatch}});Object.defineProperty(exports,"getMemoryReport",{enumerable:true,get:function(){return _store.getMemoryReport}});Object.defineProperty(exports,"memoryLeaksSyncAdapter",{enumerable:true,get:function(){return _memoryLeaksSyncAdapter.memoryLeaksSyncAdapter}});Object.defineProperty(exports,"memoryLeaksToolPreset",{enumerable:true,get:function(){return _preset.memoryLeaksToolPreset}});Object.defineProperty(exports,"startMemoryCheck",{enumerable:true,get:function(){return _store.startMemoryCheck}});Object.defineProperty(exports,"startMemoryWatch",{enumerable:true,get:function(){return _store.startMemoryWatch}});Object.defineProperty(exports,"stopMemoryCheck",{enumerable:true,get:function(){return _store.stopMemoryCheck}});Object.defineProperty(exports,"subscribeMemoryLeaks",{enumerable:true,get:function(){return _store.subscribeMemoryLeaks}});exports.useMemoryLeakWatch=useMemoryLeakWatch;var _react=require("react");var _capture=require("./engine/capture");var _preset=require("./preset");var _MemoryLeaksBody=require("./components/MemoryLeaksBody");var _MemoryLeaksToolView=require("./components/MemoryLeaksToolView");var _memoryLeaksSyncAdapter=require("./sync/memoryLeaksSyncAdapter");var _store=require("./engine/store");function useMemoryLeakWatch(e,r){(0,_react.useEffect)(()=>{if(typeof __DEV__!=="undefined"&&__DEV__)return(0,_capture.watchObject)(e,r);return void 0},[e,r])}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.memoryLeaksToolPreset=exports.createMemoryLeaksTool=exports.MemoryLeaksIcon=void 0;var _react=_interopRequireDefault(require("react"));var _sharedUi=require("@buoy-gg/shared-ui");var _MemoryLeaksBody=require("./components/MemoryLeaksBody");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const MemoryLeaksIcon=exports.MemoryLeaksIcon=_sharedUi.Bug;const memoryLeaksToolPreset=exports.memoryLeaksToolPreset=(0,_sharedUi.defineToolPreset)({id:"memory-leaks",name:"MEMORY LEAKS",description:"Find suspects when a screen leaves",slot:"both",color:"#a78bfa",icon:({size:e,color:o})=>(0,_jsxRuntime.jsx)(_sharedUi.Bug,{size:e,color:o}),component:_MemoryLeaksBody.MemoryLeaksBody,modal:{subtitle:"check what stays alive"}});const createMemoryLeaksTool=exports.createMemoryLeaksTool=(0,_sharedUi.createToolFactory)(memoryLeaksToolPreset);
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.memoryLeaksSyncAdapter=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _store=require("../engine/store");const memoryLeaksSyncAdapter=exports.memoryLeaksSyncAdapter=(0,_sharedUi.createSyncAdapter)({version:1,getSnapshot:_store.getMemoryReport,subscribe:_store.subscribeMemoryLeaks,actions:{getReport:_store.readMemoryReport,startCheck:_store.startMemoryCheck,startWatch:_store.startMemoryWatch,finishWatch:_store.finishMemoryWatch,stopCheck:_store.stopMemoryCheck,clearReports:_store.clearMemoryReports}});
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"MemoryLeaksIcon",{enumerable:true,get:function(){return _sharedUi.Bug}});Object.defineProperty(exports,"MemoryLeaksToolView",{enumerable:true,get:function(){return _MemoryLeaksToolView.MemoryLeaksToolView}});var _MemoryLeaksToolView=require("./components/MemoryLeaksToolView");var _sharedUi=require("@buoy-gg/shared-ui");
@@ -0,0 +1 @@
1
+ "use strict";import u,{useEffect as m,useState as a}from"react";import{getMemoryReport as t,subscribeMemoryLeaks as i}from"../engine/store";import{memoryLeaksSyncAdapter as c}from"../sync/memoryLeaksSyncAdapter";import{MemoryLeaksToolView as p}from"./MemoryLeaksToolView";import{jsx as f}from"react/jsx-runtime";export function MemoryLeaksBody(){const[r,e]=a(t);m(()=>i(()=>e(t())),[]);return f(p,{snapshot:r,onAction:async(s,n)=>{const o=c.actions[s];if(!o)throw new Error("That action is not here.");return o(n)}})}
@@ -0,0 +1 @@
1
+ "use strict";import K,{useState as u}from"react";import{ScrollView as V,StyleSheet as F,Text as n,TextInput as x,View as d}from"react-native";import{night as c,NightBadge as C,NightButton as S,NightCard as f,NightFootnote as p,NightSectionLabel as M}from"@buoy-gg/shared-ui";import{jsx as e,jsxs as l,Fragment as w}from"react/jsx-runtime";export function formatGrowth(r){if(r===null)return"No byte read";const m=Math.abs(r)>=1048576?"MB":"KB";return`${r>0?"+":""}${(r/(m==="MB"?1048576:1024)).toFixed(1)} ${m}`}const _=[["component","Components"],["interval","Intervals"],["timeout","Timeouts"],["listener","Listeners"],["subscription","Subscriptions"]];export function MemoryLeaksToolView({snapshot:r,onAction:m}){const[k,R]=u("/");const[T,z]=u("");const[I,L]=u("5");const[B,D]=u("");const[$,P]=u(false);const[v,E]=u(null);const[N,j]=u(false);const G=async(i,a)=>{D("");P(true);try{await m(i,a)}catch(s){D(s instanceof Error?s.message:String(s))}finally{P(false)}};const h=(i,a,s,o=false)=>e(S,{label:i,testID:`memory-${a}`,disabled:$,variant:o?"primary":"secondary",onPress:()=>void G(a,s)});const g=r?.suspects.reduce((i,a)=>i+a.liveCount,0)??0;const y=r?.status==="running";const b=r?.status==="complete";const W=y?"Check in progress":g?`${g} suspects found`:b?"No suspects above the limit":"Watch what stays alive";return l(V,{keyboardShouldPersistTaps:"handled",testID:"memory-leaks-view",style:t.root,contentContainerStyle:t.content,children:[!r?e(p,{centered:true,children:"Pick a linked device with Memory Leaks installed."}):l(w,{children:[l(f,{padded:true,children:[l(d,{style:t.spread,children:[e(n,{style:t.eyebrow,children:"SCREEN EXIT CHECK"}),e(C,{label:r.status,tone:y?"info":g?"warning":b?"accent":"neutral"})]}),e(n,{style:t.title,children:W}),l(n,{testID:"memory-status",style:t.caption,children:[r.status," \xB7 ",r.cyclesDone," loops"]}),l(d,{style:t.stats,children:[l(d,{style:t.stat,children:[e(n,{style:t.caption,children:"JS / loop"}),e(n,{testID:"memory-growth",style:t.value,children:formatGrowth(r.growthPerCycle.jsBytes)})]}),l(d,{style:t.stat,children:[e(n,{style:t.caption,children:"Native / loop"}),e(n,{testID:"memory-native-growth",style:t.value,children:formatGrowth(r.growthPerCycle.nativeBytes)})]})]}),l(d,{style:t.spread,children:[e(C,{label:r.gcProved?"GC proved":"GC not proved",tone:r.gcProved?"accent":"warning"}),l(n,{style:t.caption,children:[r.pendingGcCount," await GC"]})]})]}),!r.gcProved&&r.status!=="idle"&&e(p,{tone:"warning",children:"A blank list does not prove this screen is clean."}),e(d,{style:t.actions,children:y?l(w,{children:[r.mode==="watch"&&h("Finish watch","finishWatch",void 0,true),h("Stop check","stopCheck",{runId:r.runId})]}):l(w,{children:[e(S,{label:"Run check",testID:"memory-run-setup",variant:"primary",onPress:()=>j(!N)}),h("Watch exit","startWatch")]})}),N&&!y&&l(f,{padded:true,children:[e(n,{style:t.label,children:"Home route"}),e(x,{accessibilityLabel:"Home route",testID:"memory-baseline",style:t.input,value:k,onChangeText:R,autoCapitalize:"none"}),e(n,{style:t.label,children:"Test route"}),e(x,{accessibilityLabel:"Test route",testID:"memory-target",style:t.input,value:T,onChangeText:z,autoCapitalize:"none",placeholder:"/profile",placeholderTextColor:c.color.placeholder}),e(n,{style:t.label,children:"Loops (1 to 20)"}),e(x,{accessibilityLabel:"Loops",style:t.input,value:I,onChangeText:L,keyboardType:"numeric"}),h("Start loops","startCheck",{baselinePath:k,targetPath:T,cycles:Number(I)},true)]}),_.map(([i,a])=>{const s=r.suspects.filter(o=>o.kind===i);if(!s.length)return null;return l(d,{style:t.group,children:[e(M,{children:`${a} \xB7 ${s.reduce((o,H)=>o+H.liveCount,0)}`}),s.map(o=>l(f,{padded:true,children:[l(d,{style:t.spread,children:[e(n,{testID:`memory-suspect-${i}`,style:t.rowTitle,children:o.owner??o.appSource?.split(" \xB7 ")[0]??o.source}),e(C,{label:`${o.liveCount} live`,tone:"warning"})]}),e(n,{testID:`memory-source-${i}`,selectable:true,style:t.source,children:o.appSource??(o.kind==="component"?o.ownerPath??o.source:"App source not found. Open the add stack.")}),l(n,{style:t.caption,children:[o.route??"Route unknown"," \xB7 ",o.evidence==="survived-gc"?"Still alive after GC":o.evidence==="repeated-growth"?"Grows with each loop":"Still live at this exit"]}),o.stack&&e(S,{size:"md",variant:"ghost",label:v===o.id?"Hide stack":"Show add stack",onPress:()=>E(v===o.id?null:o.id)}),v===o.id&&e(n,{selectable:true,style:t.caption,children:o.stack})]},o.id))]},i)}),!g&&l(f,{padded:true,children:[e(n,{style:t.rowTitle,children:b?"No new suspects":"Leave a screen to check it"}),e(n,{style:t.caption,children:b?"Try more loops or watch a known leak.":"Run route loops, or watch one exit by hand."})]}),e(p,{children:"Suspects are clues. This cannot prove every leak."}),l(p,{children:[r.belowThresholdCount," below the limit \xB7 ",r.droppedCount," dropped"]}),Object.entries(r.capabilities).filter(([,i])=>!i.available).map(([i,a])=>e(p,{children:a.reason},i)),r.notes.map((i,a)=>e(p,{children:i},a)),!y&&r.status!=="idle"&&h("Clear report","clearReports")]}),!!B&&e(n,{accessibilityRole:"alert",style:t.error,children:B})]})}const t=F.create({root:{flex:1},content:{padding:c.gutter,paddingBottom:60,gap:12},spread:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8},eyebrow:{color:c.color.textSecondary,fontSize:c.font.micro,letterSpacing:.8},title:{color:c.color.text,fontSize:21,fontWeight:"600",marginTop:14,marginBottom:5},caption:{color:c.color.textSecondary,fontSize:12,lineHeight:18},stats:{flexDirection:"row",gap:10,marginVertical:16},stat:{flex:1,padding:12,borderRadius:c.radius.row,backgroundColor:c.color.surfaceElevated},value:{color:c.color.text,fontSize:21,fontWeight:"600",marginTop:5},actions:{gap:8},group:{gap:8,marginTop:6},rowTitle:{color:c.color.text,fontSize:14,fontWeight:"600",flexShrink:1},source:{color:c.color.accent,fontSize:12,lineHeight:18,marginVertical:8},label:{color:c.color.textSecondary,fontSize:13,marginVertical:8},input:{color:c.color.text,backgroundColor:c.color.surfaceElevated,borderRadius:c.radius.input,padding:12,marginBottom:10},error:{color:c.color.danger}});
@@ -0,0 +1 @@
1
+ "use strict";import{AppState as U,Dimensions as x,Keyboard as j,Linking as H,DeviceEventEmitter as O,NativeEventEmitter as I}from"react-native";import{support as c}from"./runtime";const T=globalThis;let N=null;let D=null;let z=new WeakSet;let R=new WeakMap;export function watchObject(n,u){const s=N;const m=s?.add("component",n,null,u,n,"explicit")??null;return()=>s?.exit(m)}function _(n,u){try{return Object.getOwnPropertyDescriptor(n,u)?.value}catch{return void 0}}function V(){const n=T.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!n)return false;if(D===n)return true;const u=n.onCommitFiberRoot;const s=n.onCommitFiberUnmount;const m=function(...d){try{u?.apply(this,d)}finally{try{const l=N;if(l?.capturing){const a=[d[1]?.current];let g=0;while(a.length&&g++<1e4){const e=a.pop();if(!e)continue;if(e.sibling)a.push(e.sibling);if(e.child)a.push(e.child);if(e.tag!==1||!e.stateNode||R.has(e.stateNode))continue;const i=e.type?.displayName||e.type?.name||"Class";const t=l.add("component",i,null,e.stateNode,i,"instance");R.set(e.stateNode,t)}}}catch{}}};const y=function(...d){try{s?.apply(this,d)}finally{try{const l=N;if(l?.capturing){const a=d[1];const g=a?.stateNode;if(g)l.exit(R.get(g)??null);if(a&&[0,11,15].includes(a.tag)){const e=[];const i=r=>{if(r&&typeof r==="object"&&!z.has(r)){z.add(r);e.push(r)}};i(a.memoizedProps);let t=a.memoizedState;for(let r=0;t&&r<64;r++,t=t.next){const o=t.memoizedState;if(!o||typeof o!=="object")continue;if(typeof t.queue?.dispatch==="function"){i(o);continue}if("create"in o&&"next"in o||"pending"in o&&"dispatch"in o||"impl"in o&&typeof _(o,"impl")==="function"||typeof _(o,"then")==="function")continue;if(Array.isArray(o)){if(o.length===2&&(o[1]===null||Array.isArray(o[1])))i(o[0])}else{i(o);if(Object.prototype.hasOwnProperty.call(o,"current"))i(_(o,"current"))}}const v=r=>r?.type?.displayName||r?.type?.name||r?.type?.render?.name;const w=v(a)||"Function";const k=[w];let h=a._debugOwner??a.return;for(let r=0;h&&r<12;r++,h=h._debugOwner??h.return){const o=v(h);if(o)k.unshift(o)}l.addFiber(w,e,k.join(" > "))}}}catch{}}};try{n.onCommitFiberRoot=m;n.onCommitFiberUnmount=y;if(n.onCommitFiberRoot!==m||n.onCommitFiberUnmount!==y)throw new Error("sealed")}catch{try{if(n.onCommitFiberRoot===m)n.onCommitFiberRoot=u}catch{}try{if(n.onCommitFiberUnmount===y)n.onCommitFiberUnmount=s}catch{}return false}D=n;return true}function q(){try{return V()}catch{return false}}export function capture(n){N=n;R=new WeakMap;z=new WeakSet;const u=[];const s={weakRef:c(typeof T.WeakRef==="function","This VM has no WeakRef."),finalizationRegistry:c(typeof T.FinalizationRegistry==="function","This VM has no finalizers. Weak refs still work."),reactUnmountHook:c(q(),"The React hook is missing. Use useMemoryLeakWatch."),gc:c(typeof T.gc==="function","No GC call. Suspects need three loops and time.")};let m=0;let y=null;let d=new WeakMap;const l=new Set;const a=e=>{let i=d.get(e);if(!i){i=new Map;d.set(e,i)}l.add(i);return i};const g=(e,i,t,v=false)=>{const w=e?.[i];if(typeof w!=="function"){s[t]=c(false,"This add hook is not here.");return}let k=true;function h(...r){if(!k)return w.apply(this,r);const o=t==="NativeEventEmitter"||t==="DeviceEventEmitter";if(m){if(o)y={receiver:O,event:String(r[0])};return w.apply(this,r)}y=null;m++;let f;try{f=w.apply(this,r)}finally{m--}const E=v?"change":String(r[0]);const p=n.add(v?"subscription":"listener",`${t}.${E}`,new Error().stack??null);if(!p)return f;const A=y;y=null;const C=A?.event??E;const b=a(A?.receiver??(o?O:this));const F=b.get(C)??new Set;F.add(p);b.set(C,F);const S=typeof f==="function"?f:f?.remove;const L=()=>{n.remove(p);F.delete(p);if(!F.size)b.delete(C);if(!b.size)l.delete(b)};if(typeof S!=="function"){L();s[t]=c(false,"The add hook has no remove handle.");return f}if(typeof f==="function")return function(...M){const W=S.apply(this,M);L();return W};try{f.remove=function(...M){const W=S.apply(this,M);L();return W}}catch{L();s[t]=c(false,"The remove hook is sealed.")}return f}try{e[i]=h;if(e[i]!==h)throw new Error("sealed");s[t]=c(true,"");u.push(()=>{k=false;d=new WeakMap;if(e[i]===h)e[i]=w});const r=e.removeAllListeners;if(typeof r==="function"){let o=function(f){const E=r.apply(this,arguments);if(!k)return E;const p=d.get(t==="NativeEventEmitter"||t==="DeviceEventEmitter"?O:this);if(!p)return E;for(const[A,C]of p)if(f===void 0||A===f){for(const b of C)n.remove(b);C.clear();p.delete(A);if(!p.size)l.delete(p)}return E};e.removeAllListeners=o;u.push(()=>{if(e.removeAllListeners===o)e.removeAllListeners=r})}}catch{s[t]=c(false,"This hook is sealed.")}};for(const[e,i]of[[U,"AppState"],[x,"Dimensions"],[j,"Keyboard"],[H,"Linking"],[O,"DeviceEventEmitter"],[I?.prototype,"NativeEventEmitter"]]){try{g(e,"addEventListener"in(e??{})?"addEventListener":"addListener",i)}catch{s[i]=c(false,"Could not read this add hook.")}}try{g(require("@react-native-community/netinfo").default,"addEventListener","NetInfo",true)}catch{s.NetInfo=c(false,"NetInfo is not installed.")}try{const e=new Map;const i=require("@buoy-gg/clock/timers").observeTimers(t=>{if(t.event==="add"){const v=n.add(t.kind,`Clock.${t.kind}`,t.stack??null,void 0,null,"unknown",t.delay);if(v)e.set(t.id,v)}else if(t.event!=="fire"||t.kind==="timeout"){n.remove(e.get(t.id)??null);e.delete(t.id)}});u.push(i);s.timers=c(true,"")}catch{s.timers=c(false,"Timers are not tracked. Install @buoy-gg/clock for its shared timer feed.")}return{sources:s,stop(){if(N===n)N=null;n.capturing=false;for(const e of u.splice(0).reverse()){try{e()}catch{}}for(const e of l){for(const i of e.values())i.clear();e.clear()}l.clear();n.clear()}}}
@@ -0,0 +1 @@
1
+ "use strict";export function watchObject(t,e){return()=>{}}
@@ -0,0 +1 @@
1
+ "use strict";const n=globalThis;export const now=()=>n.__buoyRealNow?.()??Date.now();export const sleep=e=>new Promise(t=>(n.__buoyRealTimers??n).setTimeout(t,e));export function stats(){try{return n.HermesInternal?.getInstrumentedStats?.()??{}}catch{return{}}}export function support(e,t){return{available:e,reason:e?null:t}}export function sample(e,t){const s=stats();let r=null;try{const i=require("@buoy-gg/perf-monitor");if(i.probeCapabilities().accurateMemory){const o=i.loadToolkit()?.getMemoryUsage();if(typeof o==="number"&&Number.isFinite(o)&&o>0)r=o*1024*1024}}catch{}return{cycle:e,at:now(),exitObserved:t,jsBytes:Number.isFinite(s.js_allocatedBytes)?s.js_allocatedBytes:null,gcCount:Number.isFinite(s.js_numGCs)?s.js_numGCs:null,nativeBytes:r}}export async function collect(){await sleep(50);const e=stats().js_numGCs;try{if(typeof n.gc==="function")n.gc()}catch{}await sleep(50);const t=stats().js_numGCs;return{count:Number.isFinite(t)?t:null,proved:Number.isFinite(e)&&Number.isFinite(t)&&t>e}}
@@ -0,0 +1 @@
1
+ "use strict";export function publicStack(l){return l?.replace(/https?:\/\/[^)\s]+:(\d+):(\d+)/g,"bundle:$1:$2").replace(/https?:\/\/[^)\s]+/g,"bundle")??null}export function appFrame(l){const t=l.find(r=>r.file&&r.lineNumber&&!r.collapse&&!/node_modules|\/packages\/(?:clock|js-top|memory-leaks|shared|devtools-floating-menu)\/|@buoy-gg|\/Libraries\//.test(r.file)&&!/^https?:|\.bundle(?:\?|$)/.test(r.file));if(!t)return null;const o=t.file.replace(/^.*\/(?:example-dev-build|example|src)\//,"");return`${(t.methodName||"App").slice(0,200)} \xB7 ${o.slice(-500)}:${t.lineNumber}`}export async function resolveSources(l){const t=[...new Set(l.map(e=>e.stack).filter(e=>!!e))];const o=new Map;let r;try{const e=require("react-native/Libraries/Core/Devtools/parseErrorStack").default;const i=require("react-native/Libraries/Core/Devtools/getDevServer").default();if(!i.bundleLoadedFromServer||!t.length)throw new Error("No source map.");const c=t.map(s=>e(s).slice(0,8));const u=new AbortController;const p=globalThis.__buoyRealTimers??globalThis;const m=new Promise((s,n)=>{r=p.setTimeout(()=>{u.abort();n(new Error("Source map timed out."))},3e3)});const d=fetch(`${i.url}symbolicate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({stack:c.flat()}),signal:u.signal}).then(s=>s.json());const b=await Promise.race([d,m]);let a=0;for(let s=0;s<t.length;s++){const n=c[s].length;o.set(t[s],appFrame(b.stack.slice(a,a+n)));a+=n}}catch{}finally{(globalThis.__buoyRealTimers??globalThis).clearTimeout(r)}return l.map(e=>({...e,appSource:e.stack?o.get(e.stack)??null:null,stack:publicStack(e.stack)}))}
@@ -0,0 +1 @@
1
+ "use strict";import{AppState as T,Platform as I}from"react-native";import{isProNow as S,subscribeAccountAccess as R}from"@buoy-gg/shared-ui";import{Tracker as _}from"./tracker";import{publicStack as B,resolveSources as P}from"./source";import{capture as j}from"./capture";import{collect as A,now as l,sample as y,sleep as W,support as M}from"./runtime";const k=new Set;const m=globalThis;let G=0;let n=g();let i=null;function g(){return{runId:"",status:"idle",mode:"loop",cyclesDone:0,capabilities:{},samples:[],growthPerCycle:{jsBytes:null,nativeBytes:null},suspects:[],pendingGcCount:0,belowThresholdCount:0,collectedCount:0,gcProved:false,droppedCount:0,notes:[]}}function b(){return typeof __DEV__!=="undefined"&&__DEV__&&I.OS!=="web"&&S()}function p(){if(!b())throw new Error("Memory checks need Pro and a native dev build.")}function d(){n={...n};for(const r of k){try{r()}catch{}}}export function subscribeMemoryLeaks(r){k.add(r);return()=>{k.delete(r)}}export function getMemoryReport(r){if(!b())return{...g(),notes:["Memory checks need Pro and a native dev build."]};const t=r?.runId;if(t&&t!==n.runId)throw new Error("That run is no longer here.");return{...n,suspects:n.suspects.map(a=>({...a,stack:B(a.stack)}))}}export function readMemoryReport(r){p();return getMemoryReport(r)}export function validateInput(r){const t=r;const a=t?.cycles??5,e=t?.settleMs??1500;for(const o of[t?.baselinePath,t?.targetPath])if(typeof o!=="string"||!o.startsWith("/")||o.length>300||/[?#]/.test(o))throw new Error("Use a route path with no query or hash.");if(t.baselinePath===t.targetPath)throw new Error("Pick two distinct routes.");if(!Number.isInteger(a)||a<1||a>20)throw new Error("Use 1 to 20 loops.");if(!Number.isInteger(e)||e<500||e>5e3)throw new Error("Use a wait from 500 to 5000 ms.");return{baselinePath:t.baselinePath,targetPath:t.targetPath,cycles:a,settleMs:e}}function E(r){p();if(i)throw new Error("A check is still running.");const t=new _(l,typeof m.WeakRef==="function"?o=>new m.WeakRef(o):void 0,5e3,r==="loop"?3:1);n={...g(),runId:`${l()}-${++G}`,status:"running",mode:r};const a=[];const e={tracker:t,cancelled:false,finishing:false,deadline:l()+18e4,stopCapture(){for(const o of a.splice(0).reverse()){try{o()}catch{}}}};i=e;try{const o=T.addEventListener("change",C=>{if(i===e&&C!=="active")u("partial","The app left the screen.")});a.push(()=>o.remove());a.push(R(()=>{if(i===e&&!b())u("partial","Pro access ended.")}));h(e);const s=j(t);a.push(s.stop);const c=m.__buoyRealTimers??m;const x=c.setTimeout(()=>{if(i===e)u("partial","The check hit its three minute limit.")},18e4);a.push(()=>c.clearTimeout(x));n.capabilities=s.sources;d();return e}catch(o){e.stopCapture();if(i===e)u("partial","Capture could not start.");throw o}}function u(r,t){const a=i;if(!a)return;a.cancelled=true;i=null;a.stopCapture();n.status=r;if(t)n.notes=[...n.notes,t];if(!n.gcProved)n.notes=[...n.notes,"GC was not proved. A blank list does not prove this screen is clean."];d()}function h(r){if(r.cancelled||i!==r)throw new Error("Check stopped.");p();if(l()>r.deadline)throw new Error("The check hit its three minute limit.")}async function f(r,t){const a=l()+t;do{h(r);await W(Math.min(100,Math.max(0,a-l())))}while(l()<a)}async function v(r,t,a){for(let s=0;s<2;s++){const c=await A();h(r);n.gcProved||=c.proved;Object.assign(n,r.tracker.inspect(c.count,c.proved))}const e=y(t,a);n.samples=[...n.samples,e];n.cyclesDone=t;n.capabilities.jsHeap=M(e.jsBytes!==null,"The VM has no live JS byte read.");n.capabilities.nativeMemory=M(e.nativeBytes!==null,"Perf Monitor has no native byte read here.");const o=n.samples[0];for(const s of["jsBytes","nativeBytes"]){n.growthPerCycle[s]=t&&o[s]!==null&&e[s]!==null?(e[s]-o[s])/t:null}d()}async function w(r,t,a){const e=l()+1e4;while(l()<e){h(r);const o=await t.getCurrentRoute();const s=c=>c.replace(/\/\([^/]+\)/g,"").replace(/\/index$/,"").replace(/\/$/,"")||"/";if(!o?.stale&&typeof o?.path==="string"&&s(o.path)===s(a))return;await f(r,100)}throw new Error(`Route did not reach ${a}. Check RouteTracker.`)}export function startMemoryCheck(r){p();const t=validateInput(r);let a;try{a=require("@buoy-gg/route-events").routeEventsSyncAdapter.actions}catch{throw new Error("Add @buoy-gg/route-events to run route loops.")}const e=E("loop");void(async()=>{try{y(-1,false);await a.navigate({path:t.baselinePath,replace:true});await w(e,a,t.baselinePath);await f(e,500);e.tracker.route=t.targetPath;e.tracker.capturing=true;await a.navigate({path:t.targetPath});await w(e,a,t.targetPath);await f(e,t.settleMs);await a.stackGoBack();await w(e,a,t.baselinePath);await f(e,t.settleMs);e.tracker.capturing=false;e.tracker.exitCycle();await v(e,0,false);e.tracker.sealBaseline();n.suspects=[];n.pendingGcCount=0;for(let s=1;s<=t.cycles;s++){h(e);e.tracker.cycle=s;e.tracker.route=t.targetPath;e.tracker.capturing=true;const c=e.tracker.exits;await a.navigate({path:t.targetPath});await w(e,a,t.targetPath);await f(e,t.settleMs);await a.stackGoBack();await w(e,a,t.baselinePath);await f(e,t.settleMs);e.tracker.capturing=false;if(e.tracker.exits===c)throw new Error("No watched object left. The screen may stay mounted. Use useMemoryLeakWatch in this screen.");e.tracker.exitCycle();await v(e,s,true)}const o=await P(n.suspects);h(e);n.suspects=o;u("complete")}catch(o){if(i===e)u("partial",o instanceof Error?o.message:String(o))}})();return{runId:n.runId,status:"running"}}export function startMemoryWatch(){const r=E("watch");r.tracker.capturing=true;r.tracker.cycle=1;n.samples=[y(0,false)];d();return{runId:n.runId,status:"running"}}export async function finishMemoryWatch(){p();const r=i;if(!r||n.mode!=="watch")throw new Error("Start a watch first.");if(r.finishing)throw new Error("This watch is being checked.");r.finishing=true;try{r.tracker.capturing=false;await f(r,1500);r.tracker.exitCycle();await v(r,1,r.tracker.exits>0);const t=await P(n.suspects);h(r);n.suspects=t;u(r.tracker.exits?"complete":"partial",r.tracker.exits?void 0:"No watched object left.");return readMemoryReport()}catch(t){if(i===r)u("partial",String(t));throw t}}export function stopMemoryCheck(r){p();const t=r?.runId;if(!t||t!==n.runId)throw new Error("Use the current runId to stop.");u("cancelled");return readMemoryReport()}export function clearMemoryReports(){p();if(i)throw new Error("Stop the check first.");n=g();d();return n}if(typeof module!=="undefined")module.hot?.dispose(()=>u("cancelled","JS reloaded."));
@@ -0,0 +1 @@
1
+ "use strict";export function getMemoryReport(){return{runId:"",status:"idle",mode:"loop",cyclesDone:0,capabilities:{},samples:[],growthPerCycle:{jsBytes:null,nativeBytes:null},suspects:[],pendingGcCount:0,belowThresholdCount:0,collectedCount:0,gcProved:false,droppedCount:0,notes:["Pick a native dev build to run checks."]}}function e(){throw new Error("Run this check on a linked native dev build.")}export const readMemoryReport=e;export const startMemoryCheck=e;export const startMemoryWatch=e;export const finishMemoryWatch=e;export const stopMemoryCheck=e;export const clearMemoryReports=e;export function subscribeMemoryLeaks(t){return()=>{}}
@@ -0,0 +1 @@
1
+ "use strict";export class Tracker{rows=new Map;seq=0;baseline=new Set;key(e){return JSON.stringify([e.kind,e.source,e.owner,e.ownerPath,e.stack])}sealBaseline(){for(const e of this.rows.keys())this.baseline.add(e)}dropped=0;collected=0;cycle=0;route=null;capturing=false;exits=0;constructor(e,i,o=5e3,n=1){this.now=e;this.makeRef=i;this.cap=o;this.minimumCycles=n}add(e,i,o=null,n,r=null,l="unknown",h){if(!this.capturing)return null;if(this.rows.size>=this.cap){this.dropped++;return null}if(n&&!this.makeRef)return null;const s=String(++this.seq);this.rows.set(s,{id:s,kind:e,source:i.slice(0,200),stack:o?.split("\n").slice(0,8).join("\n").slice(0,2e3)??null,owner:r?.slice(0,200)??null,ownerBasis:l,route:this.route?.slice(0,300)??null,at:this.now(),cycle:this.cycle,exited:false,...e==="timeout"&&h!==void 0?{dueAt:this.now()+h}:{},...n?{ref:this.makeRef(n)}:{},gcReads:0,lastGc:null});return s}addFiber(e,i,o){if(!i.length||!this.makeRef)return;const n=this.add("component",e,null,i[0],e,"fiber");const r=n?this.rows.get(n):void 0;if(r){r.refs=[r.ref,...i.slice(1).map(l=>this.makeRef(l))];r.ownerPath=o.slice(0,600);this.exit(n)}}remove(e){if(e)this.rows.delete(e)}exit(e){const i=e?this.rows.get(e):void 0;if(i&&!i.exited){i.exited=true;this.exits++}}exitCycle(){for(const e of this.rows.values())if(e.cycle===this.cycle&&!e.ref)e.exited=true}inspect(e,i){const o=new Map;let n=0;let r=0;for(const[h,s]of this.rows){if(!s.exited)continue;if(s.ref){if(!(s.refs??[s.ref]).some(d=>d.deref())){this.rows.delete(h);this.collected++;continue}if(i&&e!==null&&s.lastGc!==e){s.gcReads++;s.lastGc=e}}const t=this.key(s);const u=o.get(t)??{rows:[],cycles:new Set};u.rows.push(s);u.cycles.add(s.cycle);o.set(t,u)}const l=[];for(const{rows:h}of o.values()){const s=h.filter(c=>!this.baseline.has(c.id));if(!s.length)continue;const t=s[0];const u=this.now()-t.at;const d=new Set(s.map(c=>c.cycle));if((!t.ref||t.ownerBasis==="fiber")&&(d.size<this.minimumCycles||this.minimumCycles>1&&u<3e3||s.some(c=>c.dueAt!==void 0&&c.dueAt>this.now()))){r+=s.length;continue}const a=s.every(c=>!c.ref||c.gcReads>=2);const f=d.size>=3&&u>=3e3;if(t.ref&&!a&&!f){n+=s.length;continue}l.push({id:t.id,kind:t.kind,source:t.source,owner:t.owner,ownerBasis:t.ownerBasis,ownerPath:t.ownerPath,route:t.route,stack:t.stack,liveCount:s.length,growthCount:s.length,ageMs:u,evidence:t.ref&&a?"survived-gc":f?"repeated-growth":"still-live"})}return{suspects:l.slice(0,200),pendingGcCount:n,belowThresholdCount:r,collectedCount:this.collected,droppedCount:this.dropped+Math.max(0,l.length-200)}}clear(){this.rows.clear();this.baseline.clear()}}
@@ -0,0 +1 @@
1
+ "use strict";import{useEffect as r}from"react";import{watchObject as t}from"./engine/capture";export{memoryLeaksToolPreset,createMemoryLeaksTool,MemoryLeaksIcon}from"./preset";export{MemoryLeaksBody}from"./components/MemoryLeaksBody";export{MemoryLeaksToolView}from"./components/MemoryLeaksToolView";export{memoryLeaksSyncAdapter}from"./sync/memoryLeaksSyncAdapter";export{getMemoryReport,startMemoryCheck,startMemoryWatch,finishMemoryWatch,stopMemoryCheck,clearMemoryReports,subscribeMemoryLeaks}from"./engine/store";export function useMemoryLeakWatch(e,o){r(()=>{if(typeof __DEV__!=="undefined"&&__DEV__)return t(e,o);return void 0},[e,o])}
@@ -0,0 +1 @@
1
+ "use strict";import i from"react";import{Bug as o,createToolFactory as r,defineToolPreset as s}from"@buoy-gg/shared-ui";import{MemoryLeaksBody as a}from"./components/MemoryLeaksBody";import{jsx as c}from"react/jsx-runtime";export const MemoryLeaksIcon=o;export const memoryLeaksToolPreset=s({id:"memory-leaks",name:"MEMORY LEAKS",description:"Find suspects when a screen leaves",slot:"both",color:"#a78bfa",icon:({size:e,color:t})=>c(o,{size:e,color:t}),component:a,modal:{subtitle:"check what stays alive"}});export const createMemoryLeaksTool=r(memoryLeaksToolPreset);
@@ -0,0 +1 @@
1
+ "use strict";import{createSyncAdapter as e}from"@buoy-gg/shared-ui";import{getMemoryReport as r,readMemoryReport as t,subscribeMemoryLeaks as o,startMemoryCheck as s,startMemoryWatch as a,finishMemoryWatch as c,stopMemoryCheck as m,clearMemoryReports as p}from"../engine/store";export const memoryLeaksSyncAdapter=e({version:1,getSnapshot:r,subscribe:o,actions:{getReport:t,startCheck:s,startWatch:a,finishWatch:c,stopCheck:m,clearReports:p}});
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";export{MemoryLeaksToolView}from"./components/MemoryLeaksToolView";export{Bug as MemoryLeaksIcon}from"@buoy-gg/shared-ui";
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ export declare function MemoryLeaksBody(): React.JSX.Element;
3
+ //# sourceMappingURL=MemoryLeaksBody.d.ts.map
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { MemoryReport } from "../types";
3
+ export interface MemoryLeaksToolViewProps {
4
+ snapshot: MemoryReport | null;
5
+ onAction(action: string, params?: Record<string, unknown>): Promise<unknown>;
6
+ }
7
+ export declare function formatGrowth(bytes: number | null): string;
8
+ export declare function MemoryLeaksToolView({ snapshot, onAction }: MemoryLeaksToolViewProps): React.JSX.Element;
9
+ //# sourceMappingURL=MemoryLeaksToolView.d.ts.map
@@ -0,0 +1,8 @@
1
+ import type { Support } from "../types";
2
+ import { Tracker } from "./tracker";
3
+ export declare function watchObject(label: string, target: object): () => void;
4
+ export declare function capture(tracker: Tracker): {
5
+ sources: Record<string, Support>;
6
+ stop(): void;
7
+ };
8
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1,3 @@
1
+ /** Desktop only renders device data. It never watches its own objects. */
2
+ export declare function watchObject(_label: string, _target: object): () => void;
3
+ //# sourceMappingURL=capture.web.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type { MemorySample, Support } from "../types";
2
+ export declare const now: () => number;
3
+ export declare const sleep: (ms: number) => Promise<void>;
4
+ export declare function stats(): Record<string, number>;
5
+ export declare function support(available: boolean, reason: string): Support;
6
+ export declare function sample(cycle: number, exitObserved: boolean): MemorySample;
7
+ export declare function collect(): Promise<{
8
+ count: number | null;
9
+ proved: boolean;
10
+ }>;
11
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { MemorySuspect } from "../types";
2
+ type Frame = {
3
+ file?: string;
4
+ methodName?: string;
5
+ lineNumber?: number;
6
+ column?: number;
7
+ collapse?: boolean;
8
+ };
9
+ export declare function publicStack(stack: string | null): string | null;
10
+ export declare function appFrame(frames: Frame[]): string | null;
11
+ /** Resolve only reported suspects, while capture is off. One bounded Metro call. */
12
+ export declare function resolveSources(rows: MemorySuspect[]): Promise<MemorySuspect[]>;
13
+ export {};
14
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { MemoryCheckInput, MemoryReport } from "../types";
2
+ export declare function subscribeMemoryLeaks(fn: () => void): () => void;
3
+ export declare function getMemoryReport(params?: unknown): MemoryReport;
4
+ export declare function readMemoryReport(params?: unknown): MemoryReport;
5
+ export declare function validateInput(raw: unknown): Required<MemoryCheckInput>;
6
+ export declare function startMemoryCheck(raw: unknown): {
7
+ runId: string;
8
+ status: "running";
9
+ };
10
+ export declare function startMemoryWatch(): {
11
+ runId: string;
12
+ status: "running";
13
+ };
14
+ export declare function finishMemoryWatch(): Promise<MemoryReport>;
15
+ export declare function stopMemoryCheck(params: unknown): MemoryReport;
16
+ export declare function clearMemoryReports(): MemoryReport;
17
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,12 @@
1
+ import type { MemoryReport } from "../types";
2
+ export declare function getMemoryReport(): MemoryReport;
3
+ declare function unavailable(): never;
4
+ export declare const readMemoryReport: typeof unavailable;
5
+ export declare const startMemoryCheck: typeof unavailable;
6
+ export declare const startMemoryWatch: typeof unavailable;
7
+ export declare const finishMemoryWatch: typeof unavailable;
8
+ export declare const stopMemoryCheck: typeof unavailable;
9
+ export declare const clearMemoryReports: typeof unavailable;
10
+ export declare function subscribeMemoryLeaks(_fn: () => void): () => void;
11
+ export {};
12
+ //# sourceMappingURL=store.web.d.ts.map
@@ -0,0 +1,55 @@
1
+ import type { MemoryKind, MemorySuspect } from "../types";
2
+ export interface Ref {
3
+ deref(): object | undefined;
4
+ }
5
+ interface Row {
6
+ id: string;
7
+ kind: MemoryKind;
8
+ source: string;
9
+ owner: string | null;
10
+ ownerBasis: MemorySuspect["ownerBasis"];
11
+ route: string | null;
12
+ stack: string | null;
13
+ at: number;
14
+ cycle: number;
15
+ exited: boolean;
16
+ ownerPath?: string;
17
+ dueAt?: number;
18
+ ref?: Ref;
19
+ refs?: Ref[];
20
+ gcReads: number;
21
+ lastGc: number | null;
22
+ }
23
+ export declare class Tracker {
24
+ private now;
25
+ private makeRef?;
26
+ private cap;
27
+ private minimumCycles;
28
+ private rows;
29
+ private seq;
30
+ private baseline;
31
+ private key;
32
+ sealBaseline(): void;
33
+ dropped: number;
34
+ collected: number;
35
+ cycle: number;
36
+ route: string | null;
37
+ capturing: boolean;
38
+ exits: number;
39
+ constructor(now: () => number, makeRef?: ((target: object) => Ref) | undefined, cap?: number, minimumCycles?: number);
40
+ add(kind: MemoryKind, source: string, stack?: string | null, target?: object, owner?: string | null, ownerBasis?: Row["ownerBasis"], delay?: number): string | null;
41
+ addFiber(name: string, targets: object[], ownerPath: string): void;
42
+ remove(id: string | null): void;
43
+ exit(id: string | null): void;
44
+ exitCycle(): void;
45
+ inspect(gcCount: number | null, gcProved: boolean): {
46
+ suspects: MemorySuspect[];
47
+ pendingGcCount: number;
48
+ belowThresholdCount: number;
49
+ collectedCount: number;
50
+ droppedCount: number;
51
+ };
52
+ clear(): void;
53
+ }
54
+ export {};
55
+ //# sourceMappingURL=tracker.d.ts.map
@@ -0,0 +1,10 @@
1
+ export { memoryLeaksToolPreset, createMemoryLeaksTool, MemoryLeaksIcon } from "./preset";
2
+ export { MemoryLeaksBody } from "./components/MemoryLeaksBody";
3
+ export { MemoryLeaksToolView } from "./components/MemoryLeaksToolView";
4
+ export type { MemoryLeaksToolViewProps } from "./components/MemoryLeaksToolView";
5
+ export { memoryLeaksSyncAdapter } from "./sync/memoryLeaksSyncAdapter";
6
+ export { getMemoryReport, startMemoryCheck, startMemoryWatch, finishMemoryWatch, stopMemoryCheck, clearMemoryReports, subscribeMemoryLeaks } from "./engine/store";
7
+ export type { MemoryReport, MemoryCheckInput, MemoryKind, MemorySuspect, MemorySample } from "./types";
8
+ /** Watch an app-owned object. Its target must stay stable across renders. */
9
+ export declare function useMemoryLeakWatch(label: string, target: object): void;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { MemoryLeaksBody } from "./components/MemoryLeaksBody";
3
+ export declare const MemoryLeaksIcon: import("packages/floating-tools-core/lib/typescript/module/icons/icon-renderer").BuoyIconComponent;
4
+ export declare const memoryLeaksToolPreset: {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ slot: "both";
9
+ color: string;
10
+ icon: ({ size, color }: {
11
+ size: number;
12
+ color?: string;
13
+ }) => React.JSX.Element;
14
+ component: typeof MemoryLeaksBody;
15
+ modal: {
16
+ subtitle: string;
17
+ };
18
+ };
19
+ export declare const createMemoryLeaksTool: (options?: (Record<never, never> & import("@buoy-gg/shared-ui").ToolPresetOverrides) | undefined) => {
20
+ id: string;
21
+ name: string;
22
+ description: string;
23
+ slot: "both";
24
+ color: string;
25
+ icon: ({ size, color }: {
26
+ size: number;
27
+ color?: string;
28
+ }) => React.JSX.Element;
29
+ component: typeof MemoryLeaksBody;
30
+ modal: {
31
+ subtitle: string;
32
+ };
33
+ };
34
+ //# sourceMappingURL=preset.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { readMemoryReport, startMemoryCheck, startMemoryWatch, finishMemoryWatch, stopMemoryCheck, clearMemoryReports } from "../engine/store";
2
+ export declare const memoryLeaksSyncAdapter: import("@buoy-gg/shared-ui").SyncAdapter<import("..").MemoryReport, {
3
+ getReport: typeof readMemoryReport;
4
+ startCheck: typeof startMemoryCheck;
5
+ startWatch: typeof startMemoryWatch;
6
+ finishWatch: typeof finishMemoryWatch;
7
+ stopCheck: typeof stopMemoryCheck;
8
+ clearReports: typeof clearMemoryReports;
9
+ }>;
10
+ //# sourceMappingURL=memoryLeaksSyncAdapter.d.ts.map
@@ -0,0 +1,54 @@
1
+ export type MemoryKind = "component" | "interval" | "timeout" | "listener" | "subscription";
2
+ export type Support = {
3
+ available: boolean;
4
+ reason: string | null;
5
+ };
6
+ export interface MemoryCheckInput {
7
+ baselinePath: string;
8
+ targetPath: string;
9
+ cycles?: number;
10
+ settleMs?: number;
11
+ }
12
+ export interface MemorySuspect {
13
+ id: string;
14
+ kind: MemoryKind;
15
+ source: string;
16
+ owner: string | null;
17
+ ownerBasis: "instance" | "explicit" | "fiber" | "unknown";
18
+ route: string | null;
19
+ ownerPath?: string;
20
+ appSource?: string | null;
21
+ stack: string | null;
22
+ liveCount: number;
23
+ growthCount: number;
24
+ ageMs: number;
25
+ evidence: "survived-gc" | "still-live" | "repeated-growth";
26
+ }
27
+ export interface MemorySample {
28
+ cycle: number;
29
+ at: number;
30
+ jsBytes: number | null;
31
+ nativeBytes: number | null;
32
+ gcCount: number | null;
33
+ exitObserved: boolean;
34
+ }
35
+ export interface MemoryReport {
36
+ runId: string;
37
+ status: "idle" | "running" | "complete" | "partial" | "cancelled" | "failed";
38
+ mode: "loop" | "watch";
39
+ cyclesDone: number;
40
+ capabilities: Record<string, Support>;
41
+ samples: MemorySample[];
42
+ growthPerCycle: {
43
+ jsBytes: number | null;
44
+ nativeBytes: number | null;
45
+ };
46
+ suspects: MemorySuspect[];
47
+ pendingGcCount: number;
48
+ belowThresholdCount: number;
49
+ collectedCount: number;
50
+ gcProved: boolean;
51
+ droppedCount: number;
52
+ notes: string[];
53
+ }
54
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,5 @@
1
+ export { MemoryLeaksToolView } from "./components/MemoryLeaksToolView";
2
+ export type { MemoryLeaksToolViewProps } from "./components/MemoryLeaksToolView";
3
+ export type { MemoryReport } from "./types";
4
+ export { Bug as MemoryLeaksIcon } from "@buoy-gg/shared-ui";
5
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ "use client";var U=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var xe=(e,t)=>{for(var n in t)U(e,n,{get:t[n],enumerable:true})},_=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ge(t))!we.call(e,a)&&a!==n&&U(e,a,{get:()=>t[a],enumerable:!(s=ve(t,a))||s.enumerable});return e},ke=(e,t,n)=>(_(e,t,"default"),n&&_(n,t,"default"));var Y=(()=>{try{let e=globalThis.Capacitor;if(e&&typeof e.isNativePlatform=="function"&&e.isNativePlatform())return e.DEBUG===true}catch{}return process.env.NODE_ENV!=="production"})();typeof window<"u"&&typeof globalThis.global>"u"&&(globalThis.global=globalThis);import{useEffect as Se}from"react";function Ce(e,t){return()=>{}}import{Bug as J,createToolFactory as Ee,defineToolPreset as Te}from"@buoy-gg/shared-ui/web";import{useEffect as Le,useState as Pe}from"react";function O(){return{runId:"",status:"idle",mode:"loop",cyclesDone:0,capabilities:{},samples:[],growthPerCycle:{jsBytes:null,nativeBytes:null},suspects:[],pendingGcCount:0,belowThresholdCount:0,collectedCount:0,gcProved:false,droppedCount:0,notes:["Pick a native dev build to run checks."]}}function L(){throw new Error("Run this check on a linked native dev build.")}var Ae=L,X=L,q=L,Q=L,Z=L,ee=L;function V(e){return()=>{}}import{createSyncAdapter as Be}from"@buoy-gg/shared-ui/web";var te=Be({version:1,getSnapshot:O,subscribe:V,actions:{getReport:Ae,startCheck:X,startWatch:q,finishWatch:Q,stopCheck:Z,clearReports:ee}});import{useState as E}from"react";var oe={};xe(oe,{AccessibilityInfo:()=>Re,ActivityIndicator:()=>Dt,Alert:()=>zt,Animated:()=>Bt,AppRegistry:()=>Oe,AppState:()=>De,Appearance:()=>$e,BackHandler:()=>je,Button:()=>jt,CheckBox:()=>et,Clipboard:()=>Ve,DeviceEventEmitter:()=>ft,Dimensions:()=>ze,Easing:()=>Ke,FlatList:()=>tt,I18nManager:()=>We,Image:()=>ot,ImageBackground:()=>at,InteractionManager:()=>He,Keyboard:()=>Fe,KeyboardAvoidingView:()=>nt,LayoutAnimation:()=>Ge,Linking:()=>Ue,LogBox:()=>yt,Modal:()=>$t,NativeEventEmitter:()=>_e,NativeModules:()=>Ne,PanResponder:()=>Ye,Picker:()=>rt,PixelRatio:()=>Je,Platform:()=>Xe,Pressable:()=>Rt,ProgressBar:()=>it,RefreshControl:()=>st,SafeAreaView:()=>lt,ScrollView:()=>le,SectionList:()=>ct,Share:()=>qe,StatusBar:()=>dt,StyleSheet:()=>se,Switch:()=>Ot,Text:()=>h,TextInput:()=>D,Touchable:()=>ut,TouchableHighlight:()=>Mt,TouchableNativeFeedback:()=>pt,TouchableOpacity:()=>It,TouchableWithoutFeedback:()=>Nt,UIManager:()=>Qe,Vibration:()=>Ze,View:()=>k,VirtualizedList:()=>ht,YellowBox:()=>mt,findNodeHandle:()=>Vt,unstable_createElement:()=>Me,useColorScheme:()=>bt,useLocaleContext:()=>vt,useWindowDimensions:()=>gt});ke(oe,Ie);import*as Ie from"react-native-web";import{unstable_createElement as Me,NativeModules as Ne,AccessibilityInfo as Re,Appearance as $e,AppRegistry as Oe,AppState as De,BackHandler as je,Clipboard as Ve,Dimensions as ze,Easing as Ke,I18nManager as We,Keyboard as Fe,InteractionManager as He,LayoutAnimation as Ge,Linking as Ue,NativeEventEmitter as _e,PanResponder as Ye,PixelRatio as Je,Platform as Xe,Share as qe,UIManager as Qe,Vibration as Ze,CheckBox as et,FlatList as tt,Image as ot,ImageBackground as at,KeyboardAvoidingView as nt,Picker as rt,ProgressBar as it,RefreshControl as st,SafeAreaView as lt,SectionList as ct,StatusBar as dt,Touchable as ut,TouchableNativeFeedback as pt,VirtualizedList as ht,YellowBox as mt,LogBox as yt,DeviceEventEmitter as ft,useColorScheme as bt,useLocaleContext as vt,useWindowDimensions as gt}from"react-native-web";import*as v from"react-native-web";import{createElement as z,forwardRef as ae,useCallback as wt,useRef as xt}from"react";import{useEffect as kt,useState as St}from"react";import{createPortal as Ct}from"react-dom";var Et='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';import{jsx as Tt}from"react/jsx-runtime";var N=Symbol.for("@buoy-gg/portal-host"),ne=e=>{if(!(e instanceof Element))return false;if(e.closest("input, textarea, select"))return true;let t=e.closest("[contenteditable]")?.getAttribute("contenteditable")?.toLowerCase();return t===""||t==="true"||t==="plaintext-only"};function re(){let e=document;if(e[N])return e[N].host;let t=document.createElement("div");t.dataset.buoyPortalHost="",Object.assign(t.style,{position:"fixed",inset:"0",zIndex:"2147483647",pointerEvents:"none",fontFamily:Et});let n=()=>{let o=document.fullscreenElement,u=o&&o.tagName!=="CANVAS"?o:document.body;t.parentElement!==u&&u.appendChild(t)},s=new Map,a=o=>{(!t.contains(o.target)||!ne(o.target))&&s.set(o.code||o.key,{key:o.key,code:o.code,location:o.location,keyCode:o.keyCode,which:o.which,altKey:o.altKey,ctrlKey:o.ctrlKey,metaKey:o.metaKey,shiftKey:o.shiftKey})},d=o=>{s.delete(o.code||o.key)},b=()=>s.clear(),x=()=>{let o=[...s.values()];s.clear();for(let u of o)document.dispatchEvent(new KeyboardEvent("keyup",{...u,bubbles:true}))},l=o=>{ne(o.target)&&o.stopPropagation()},p=o=>o.stopPropagation();return n(),document.addEventListener("fullscreenchange",n),document.addEventListener("keydown",a,true),document.addEventListener("keyup",d,true),window.addEventListener("blur",b),t.addEventListener("focusin",x),t.addEventListener("keydown",l),t.addEventListener("keyup",l),t.addEventListener("keypress",l),t.addEventListener("wheel",p,{passive:true}),e[N]={host:t,users:0,dispose:()=>{document.removeEventListener("fullscreenchange",n),document.removeEventListener("keydown",a,true),document.removeEventListener("keyup",d,true),window.removeEventListener("blur",b),t.remove(),delete e[N]}},t}function Lt({children:e}){let[t,n]=St(null);return kt(()=>{let s=re(),a=document[N];return a.users++,n(s),()=>{--a.users===0&&a.dispose()}},[]),t?Ct(Tt("div",{"data-buoy-root":"",style:{display:"contents",pointerEvents:"auto"},children:e}),t):null}var P=v,Pt=["shadowColor","shadowOffset","shadowOpacity","shadowRadius","textShadowColor","textShadowOffset","textShadowRadius"],A=e=>typeof e=="number"?`${e}px`:e;function K(e){if(Array.isArray(e)){let u=e.map(K);return u.some((C,T)=>C!==e[T])?u:e}if(!e||typeof e!="object"||!Pt.some(u=>e[u]!=null))return e;let{shadowColor:t,shadowOffset:n,shadowOpacity:s,shadowRadius:a,textShadowColor:d,textShadowOffset:b,textShadowRadius:x,...l}=e;if(t!=null||n!=null||s!=null||a!=null){let u=t??"black",C=s==null?u:`color-mix(in srgb, ${u} ${Math.min(s,1)*100}%, transparent)`,T=`${A(n?.width??0)} ${A(n?.height??0)} ${A(a??0)} ${C}`;l.boxShadow=typeof l.boxShadow=="string"?`${l.boxShadow}, ${T}`:l.boxShadow??T}let{width:p=0,height:o=0}=b??{};if(d&&(p||o||x)){let u=`${A(p)} ${A(o)} ${A(x??0)} ${d}`;l.textShadow=l.textShadow?`${l.textShadow}, ${u}`:u}return l}var At=P.StyleSheet.create({auto:{pointerEvents:"auto"},none:{pointerEvents:"none"},"box-none":{pointerEvents:"box-none"},"box-only":{pointerEvents:"box-only"}});function ie(e,t){if(typeof e=="function")return s=>ie(e(s),t);let n=K(e);return t?[n,At[t]]:n}var se={...P.StyleSheet,create:e=>P.StyleSheet.create(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,K(n)])))};function w(e){let t=ae((s,a)=>{let{accessibilityState:d,accessibilityValue:b,pointerEvents:x,...l}=s;for(let o of["busy","checked","disabled","expanded","selected"])d?.[o]!==void 0&&l[`aria-${o}`]===void 0&&(l[`aria-${o}`]=d[o]);for(let[o,u]of Object.entries({min:"valuemin",max:"valuemax",now:"valuenow",text:"valuetext"}))b?.[o]!==void 0&&l[`aria-${u}`]===void 0&&(l[`aria-${u}`]=b[o]);let p=ie(l.style,x);return p!==l.style&&(l.style=p),z(e,{...l,ref:a})}),n=e.State;return n&&Object.assign(t,{State:n}),t.displayName=e.displayName??e.name??"NativeWebView",t}var k=w(v.View),h=w(v.Text),Bt={...P.Animated,View:P.Animated.createAnimatedComponent(k),Text:P.Animated.createAnimatedComponent(h)},D=w(v.TextInput),It=w(v.TouchableOpacity),Mt=w(v.TouchableHighlight),Nt=w(v.TouchableWithoutFeedback),Rt=w(v.Pressable),le=w(v.ScrollView),ce=ae((e,t)=>{let n=xt(null),s=wt(a=>{if(a){let d=a;for(;d.parentElement&&d.parentElement!==document.body;)d=d.parentElement;d.parentElement===document.body&&(n.current=d,Object.assign(d.style,{position:"relative",zIndex:"2147483647",pointerEvents:"auto"}),re().appendChild(d))}else n.current&&(document.body.appendChild(n.current),n.current=null);typeof t=="function"?t(a):t&&(t.current=a)},[t]);return z(Lt,null,z(v.Modal,{...e,ref:s}))});ce.displayName="Modal";var $t=w(ce),Ot=w(v.Switch),Dt=w(v.ActivityIndicator),jt=w(v.Button),Vt=v.findNodeHandle,zt={alert(e,t,n,s){if(typeof document>"u")return;let a=document.createElement("dialog");a.dataset.buoyRoot="",a.setAttribute("aria-label",e),a.style.cssText="position:fixed;inset:0;margin:auto;width:min(440px,90vw);padding:24px;border:1px solid #475569;border-radius:14px;background:#17212e;color:#f1f5f9;font:14px system-ui;box-shadow:0 20px 80px #0008;z-index:2147483647";let d=document.createElement("h2");if(d.textContent=e,d.style.cssText="font-size:18px;margin:0 0 12px",a.appendChild(d),t){let p=document.createElement("p");p.textContent=t,p.style.cssText="line-height:1.5;white-space:pre-wrap;margin:0 0 20px",a.appendChild(p)}let b=document.createElement("div");b.style.cssText="display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px";let x=n?.length?n:[{text:"OK"}],l=()=>{a.close?.(),a.remove()};for(let p of x){let o=document.createElement("button");o.type="button",o.textContent=p.text??"OK",o.style.cssText=`padding:9px 14px;border:1px solid #64748b;border-radius:7px;color:white;background:${p.style==="destructive"?"#a52b38":"#283a4d"};cursor:pointer;font:inherit`,o.onclick=()=>{l(),p.onPress?.()},b.appendChild(o)}a.addEventListener("cancel",p=>{p.preventDefault(),s?.cancelable!==false&&(l(),s?.onDismiss?.())}),a.appendChild(b),document.body.appendChild(a),a.showModal?a.showModal():a.setAttribute("open","")}};import{night as f,NightBadge as W,NightButton as F,NightCard as j,NightFootnote as B,NightSectionLabel as Kt}from"@buoy-gg/shared-ui/web";import{Fragment as H,jsx as r,jsxs as m}from"react/jsx-runtime";function de(e){if(e===null)return"No byte read";let t=Math.abs(e)>=1048576?"MB":"KB";return`${e>0?"+":""}${(e/(t==="MB"?1048576:1024)).toFixed(1)} ${t}`}var Wt=[["component","Components"],["interval","Intervals"],["timeout","Timeouts"],["listener","Listeners"],["subscription","Subscriptions"]];function ue({snapshot:e,onAction:t}){let[n,s]=E("/"),[a,d]=E(""),[b,x]=E("5"),[l,p]=E(""),[o,u]=E(false),[C,T]=E(null),[G,me]=E(false),ye=async(y,g)=>{p(""),u(true);try{await t(y,g)}catch(S){p(S instanceof Error?S.message:String(S))}finally{u(false)}},I=(y,g,S,c=false)=>r(F,{label:y,testID:`memory-${g}`,disabled:o,variant:c?"primary":"secondary",onPress:()=>void ye(g,S)}),R=e?.suspects.reduce((y,g)=>y+g.liveCount,0)??0,M=e?.status==="running",$=e?.status==="complete",fe=M?"Check in progress":R?`${R} suspects found`:$?"No suspects above the limit":"Watch what stays alive";return m(le,{keyboardShouldPersistTaps:"handled",testID:"memory-leaks-view",style:i.root,contentContainerStyle:i.content,children:[e?m(H,{children:[m(j,{padded:true,children:[m(k,{style:i.spread,children:[r(h,{style:i.eyebrow,children:"SCREEN EXIT CHECK"}),r(W,{label:e.status,tone:M?"info":R?"warning":$?"accent":"neutral"})]}),r(h,{style:i.title,children:fe}),m(h,{testID:"memory-status",style:i.caption,children:[e.status," \xB7 ",e.cyclesDone," loops"]}),m(k,{style:i.stats,children:[m(k,{style:i.stat,children:[r(h,{style:i.caption,children:"JS / loop"}),r(h,{testID:"memory-growth",style:i.value,children:de(e.growthPerCycle.jsBytes)})]}),m(k,{style:i.stat,children:[r(h,{style:i.caption,children:"Native / loop"}),r(h,{testID:"memory-native-growth",style:i.value,children:de(e.growthPerCycle.nativeBytes)})]})]}),m(k,{style:i.spread,children:[r(W,{label:e.gcProved?"GC proved":"GC not proved",tone:e.gcProved?"accent":"warning"}),m(h,{style:i.caption,children:[e.pendingGcCount," await GC"]})]})]}),!e.gcProved&&e.status!=="idle"&&r(B,{tone:"warning",children:"A blank list does not prove this screen is clean."}),r(k,{style:i.actions,children:M?m(H,{children:[e.mode==="watch"&&I("Finish watch","finishWatch",void 0,true),I("Stop check","stopCheck",{runId:e.runId})]}):m(H,{children:[r(F,{label:"Run check",testID:"memory-run-setup",variant:"primary",onPress:()=>me(!G)}),I("Watch exit","startWatch")]})}),G&&!M&&m(j,{padded:true,children:[r(h,{style:i.label,children:"Home route"}),r(D,{accessibilityLabel:"Home route",testID:"memory-baseline",style:i.input,value:n,onChangeText:s,autoCapitalize:"none"}),r(h,{style:i.label,children:"Test route"}),r(D,{accessibilityLabel:"Test route",testID:"memory-target",style:i.input,value:a,onChangeText:d,autoCapitalize:"none",placeholder:"/profile",placeholderTextColor:f.color.placeholder}),r(h,{style:i.label,children:"Loops (1 to 20)"}),r(D,{accessibilityLabel:"Loops",style:i.input,value:b,onChangeText:x,keyboardType:"numeric"}),I("Start loops","startCheck",{baselinePath:n,targetPath:a,cycles:Number(b)},true)]}),Wt.map(([y,g])=>{let S=e.suspects.filter(c=>c.kind===y);return S.length?m(k,{style:i.group,children:[r(Kt,{children:`${g} \xB7 ${S.reduce((c,be)=>c+be.liveCount,0)}`}),S.map(c=>m(j,{padded:true,children:[m(k,{style:i.spread,children:[r(h,{testID:`memory-suspect-${y}`,style:i.rowTitle,children:c.owner??c.appSource?.split(" \xB7 ")[0]??c.source}),r(W,{label:`${c.liveCount} live`,tone:"warning"})]}),r(h,{testID:`memory-source-${y}`,selectable:true,style:i.source,children:c.appSource??(c.kind==="component"?c.ownerPath??c.source:"App source not found. Open the add stack.")}),m(h,{style:i.caption,children:[c.route??"Route unknown"," \xB7 ",c.evidence==="survived-gc"?"Still alive after GC":c.evidence==="repeated-growth"?"Grows with each loop":"Still live at this exit"]}),c.stack&&r(F,{size:"md",variant:"ghost",label:C===c.id?"Hide stack":"Show add stack",onPress:()=>T(C===c.id?null:c.id)}),C===c.id&&r(h,{selectable:true,style:i.caption,children:c.stack})]},c.id))]},y):null}),!R&&m(j,{padded:true,children:[r(h,{style:i.rowTitle,children:$?"No new suspects":"Leave a screen to check it"}),r(h,{style:i.caption,children:$?"Try more loops or watch a known leak.":"Run route loops, or watch one exit by hand."})]}),r(B,{children:"Suspects are clues. This cannot prove every leak."}),m(B,{children:[e.belowThresholdCount," below the limit \xB7 ",e.droppedCount," dropped"]}),Object.entries(e.capabilities).filter(([,y])=>!y.available).map(([y,g])=>r(B,{children:g.reason},y)),e.notes.map((y,g)=>r(B,{children:y},g)),!M&&e.status!=="idle"&&I("Clear report","clearReports")]}):r(B,{centered:true,children:"Pick a linked device with Memory Leaks installed."}),!!l&&r(h,{accessibilityRole:"alert",style:i.error,children:l})]})}var i=se.create({root:{flex:1},content:{padding:f.gutter,paddingBottom:60,gap:12},spread:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8},eyebrow:{color:f.color.textSecondary,fontSize:f.font.micro,letterSpacing:.8},title:{color:f.color.text,fontSize:21,fontWeight:"600",marginTop:14,marginBottom:5},caption:{color:f.color.textSecondary,fontSize:12,lineHeight:18},stats:{flexDirection:"row",gap:10,marginVertical:16},stat:{flex:1,padding:12,borderRadius:f.radius.row,backgroundColor:f.color.surfaceElevated},value:{color:f.color.text,fontSize:21,fontWeight:"600",marginTop:5},actions:{gap:8},group:{gap:8,marginTop:6},rowTitle:{color:f.color.text,fontSize:14,fontWeight:"600",flexShrink:1},source:{color:f.color.accent,fontSize:12,lineHeight:18,marginVertical:8},label:{color:f.color.textSecondary,fontSize:13,marginVertical:8},input:{color:f.color.text,backgroundColor:f.color.surfaceElevated,borderRadius:f.radius.input,padding:12,marginBottom:10},error:{color:f.color.danger}});import{jsx as Ft}from"react/jsx-runtime";function pe(){let[e,t]=Pe(O);return Le(()=>V(()=>t(O())),[]),Ft(ue,{snapshot:e,onAction:async(n,s)=>{let a=te.actions[n];if(!a)throw new Error("That action is not here.");return a(s)}})}import{jsx as Ht}from"react/jsx-runtime";var Gt=J,he=Te({id:"memory-leaks",name:"MEMORY LEAKS",description:"Find suspects when a screen leaves",slot:"both",color:"#a78bfa",icon:({size:e,color:t})=>Ht(J,{size:e,color:t}),component:pe,modal:{subtitle:"check what stays alive"}}),Ut=Ee(he);function _t(e,t){Se(()=>{if(typeof Y<"u"&&Y)return Ce(e,t)},[e,t])}export{pe as MemoryLeaksBody,Gt as MemoryLeaksIcon,ue as MemoryLeaksToolView,ee as clearMemoryReports,Ut as createMemoryLeaksTool,Q as finishMemoryWatch,O as getMemoryReport,te as memoryLeaksSyncAdapter,he as memoryLeaksToolPreset,X as startMemoryCheck,q as startMemoryWatch,Z as stopMemoryCheck,V as subscribeMemoryLeaks,_t as useMemoryLeakWatch};
package/package.json CHANGED
@@ -1,6 +1,99 @@
1
1
  {
2
2
  "name": "@buoy-gg/memory-leaks",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "7.0.80",
4
+ "description": "Check what stays alive after a screen leaves.",
5
+ "main": "lib/commonjs/index.js",
6
+ "module": "lib/module/index.js",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "lib/module/index.js",
9
+ "source": "src/index.tsx",
10
+ "files": [
11
+ "lib"
12
+ ],
13
+ "sideEffects": false,
14
+ "dependencies": {
15
+ "@buoy-gg/shared-ui": "7.0.80"
16
+ },
17
+ "peerDependencies": {
18
+ "react": "*"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "^19.1.0",
22
+ "@types/react-native": "^0.73.0",
23
+ "typescript": "~5.8.3"
24
+ },
25
+ "react-native-builder-bob": {
26
+ "source": "src",
27
+ "output": "lib",
28
+ "targets": [
29
+ "commonjs",
30
+ "module",
31
+ [
32
+ "typescript",
33
+ {
34
+ "project": "tsconfig.build.json"
35
+ }
36
+ ]
37
+ ]
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/LovesWorking/react-native-buoy.git",
42
+ "directory": "packages/memory-leaks"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/LovesWorking/react-native-buoy/issues"
46
+ },
47
+ "homepage": "https://github.com/LovesWorking/react-native-buoy/tree/main/packages/memory-leaks#readme",
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "tag": "latest"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "react-native": {
54
+ "optional": true
55
+ },
56
+ "@buoy-gg/clock": {
57
+ "optional": true
58
+ },
59
+ "@buoy-gg/route-events": {
60
+ "optional": true
61
+ },
62
+ "@buoy-gg/perf-monitor": {
63
+ "optional": true
64
+ },
65
+ "@react-native-community/netinfo": {
66
+ "optional": true
67
+ }
68
+ },
69
+ "exports": {
70
+ ".": {
71
+ "react-native": "./lib/module/index.js",
72
+ "browser": {
73
+ "types": "./lib/typescript/index.d.ts",
74
+ "default": "./lib/web/index.mjs"
75
+ },
76
+ "import": {
77
+ "types": "./lib/typescript/index.d.ts",
78
+ "default": "./lib/module/index.js"
79
+ },
80
+ "require": {
81
+ "types": "./lib/typescript/index.d.ts",
82
+ "default": "./lib/commonjs/index.js"
83
+ }
84
+ },
85
+ "./web": {
86
+ "types": "./lib/typescript/index.d.ts",
87
+ "default": "./lib/web/index.mjs"
88
+ },
89
+ "./package.json": "./package.json"
90
+ },
91
+ "scripts": {
92
+ "build": "bob build && pnpm run build:web",
93
+ "typecheck": "tsc --noEmit",
94
+ "clean": "rimraf lib",
95
+ "test": "pnpm run typecheck",
96
+ "postinstall": "echo \"Run pnpm build to compile this package\"",
97
+ "build:web": "node ../../scripts/build-web.mjs memory-leaks"
98
+ }
6
99
  }