@buoy-gg/js-top 7.0.40 → 7.0.42

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 (118) hide show
  1. package/README.md +12 -67
  2. package/lib/commonjs/index.js +1 -97
  3. package/lib/commonjs/js-top/components/BusySparkline.js +1 -56
  4. package/lib/commonjs/js-top/components/JsTopHudOverlay.js +1 -79
  5. package/lib/commonjs/js-top/components/JsTopModal.js +1 -349
  6. package/lib/commonjs/js-top/components/TaskDetailView.js +1 -330
  7. package/lib/commonjs/js-top/components/TaskTable.js +1 -360
  8. package/lib/commonjs/js-top/engine/JsTopController.js +1 -350
  9. package/lib/commonjs/js-top/engine/aggregator.js +1 -148
  10. package/lib/commonjs/js-top/engine/busyProbe.js +1 -101
  11. package/lib/commonjs/js-top/engine/frameStack.js +1 -133
  12. package/lib/commonjs/js-top/engine/longtasks.js +1 -141
  13. package/lib/commonjs/js-top/engine/now.js +1 -38
  14. package/lib/commonjs/js-top/engine/originRegistry.js +1 -402
  15. package/lib/commonjs/js-top/engine/patches/bridge.js +1 -60
  16. package/lib/commonjs/js-top/engine/patches/microtasks.js +1 -27
  17. package/lib/commonjs/js-top/engine/patches/promises.js +1 -65
  18. package/lib/commonjs/js-top/engine/patches/timers.js +1 -66
  19. package/lib/commonjs/js-top/engine/state.js +1 -63
  20. package/lib/commonjs/js-top/engine/symbolicate.js +1 -95
  21. package/lib/commonjs/js-top/sync/jsTopSyncAdapter.js +1 -74
  22. package/lib/commonjs/js-top/types/index.js +1 -46
  23. package/lib/commonjs/js-top/utils/preferences.js +1 -31
  24. package/lib/commonjs/preset.js +1 -75
  25. package/lib/module/index.js +1 -47
  26. package/lib/module/js-top/components/BusySparkline.js +1 -52
  27. package/lib/module/js-top/components/JsTopHudOverlay.js +1 -75
  28. package/lib/module/js-top/components/JsTopModal.js +1 -345
  29. package/lib/module/js-top/components/TaskDetailView.js +1 -326
  30. package/lib/module/js-top/components/TaskTable.js +1 -353
  31. package/lib/module/js-top/engine/JsTopController.js +1 -347
  32. package/lib/module/js-top/engine/aggregator.js +1 -136
  33. package/lib/module/js-top/engine/busyProbe.js +1 -94
  34. package/lib/module/js-top/engine/frameStack.js +1 -126
  35. package/lib/module/js-top/engine/longtasks.js +1 -133
  36. package/lib/module/js-top/engine/now.js +1 -33
  37. package/lib/module/js-top/engine/originRegistry.js +1 -388
  38. package/lib/module/js-top/engine/patches/bridge.js +1 -56
  39. package/lib/module/js-top/engine/patches/microtasks.js +1 -23
  40. package/lib/module/js-top/engine/patches/promises.js +1 -62
  41. package/lib/module/js-top/engine/patches/timers.js +1 -62
  42. package/lib/module/js-top/engine/state.js +1 -54
  43. package/lib/module/js-top/engine/symbolicate.js +1 -91
  44. package/lib/module/js-top/sync/jsTopSyncAdapter.js +1 -70
  45. package/lib/module/js-top/types/index.js +1 -42
  46. package/lib/module/js-top/utils/preferences.js +1 -26
  47. package/lib/module/preset.js +1 -71
  48. package/lib/web/index.mjs +2 -0
  49. package/package.json +39 -5
  50. package/lib/commonjs/index.js.map +0 -1
  51. package/lib/commonjs/js-top/components/BusySparkline.js.map +0 -1
  52. package/lib/commonjs/js-top/components/JsTopHudOverlay.js.map +0 -1
  53. package/lib/commonjs/js-top/components/JsTopModal.js.map +0 -1
  54. package/lib/commonjs/js-top/components/TaskDetailView.js.map +0 -1
  55. package/lib/commonjs/js-top/components/TaskTable.js.map +0 -1
  56. package/lib/commonjs/js-top/engine/JsTopController.js.map +0 -1
  57. package/lib/commonjs/js-top/engine/aggregator.js.map +0 -1
  58. package/lib/commonjs/js-top/engine/busyProbe.js.map +0 -1
  59. package/lib/commonjs/js-top/engine/frameStack.js.map +0 -1
  60. package/lib/commonjs/js-top/engine/longtasks.js.map +0 -1
  61. package/lib/commonjs/js-top/engine/now.js.map +0 -1
  62. package/lib/commonjs/js-top/engine/originRegistry.js.map +0 -1
  63. package/lib/commonjs/js-top/engine/patches/bridge.js.map +0 -1
  64. package/lib/commonjs/js-top/engine/patches/microtasks.js.map +0 -1
  65. package/lib/commonjs/js-top/engine/patches/promises.js.map +0 -1
  66. package/lib/commonjs/js-top/engine/patches/timers.js.map +0 -1
  67. package/lib/commonjs/js-top/engine/state.js.map +0 -1
  68. package/lib/commonjs/js-top/engine/symbolicate.js.map +0 -1
  69. package/lib/commonjs/js-top/sync/jsTopSyncAdapter.js.map +0 -1
  70. package/lib/commonjs/js-top/types/index.js.map +0 -1
  71. package/lib/commonjs/js-top/utils/preferences.js.map +0 -1
  72. package/lib/commonjs/preset.js.map +0 -1
  73. package/lib/module/index.js.map +0 -1
  74. package/lib/module/js-top/components/BusySparkline.js.map +0 -1
  75. package/lib/module/js-top/components/JsTopHudOverlay.js.map +0 -1
  76. package/lib/module/js-top/components/JsTopModal.js.map +0 -1
  77. package/lib/module/js-top/components/TaskDetailView.js.map +0 -1
  78. package/lib/module/js-top/components/TaskTable.js.map +0 -1
  79. package/lib/module/js-top/engine/JsTopController.js.map +0 -1
  80. package/lib/module/js-top/engine/aggregator.js.map +0 -1
  81. package/lib/module/js-top/engine/busyProbe.js.map +0 -1
  82. package/lib/module/js-top/engine/frameStack.js.map +0 -1
  83. package/lib/module/js-top/engine/longtasks.js.map +0 -1
  84. package/lib/module/js-top/engine/now.js.map +0 -1
  85. package/lib/module/js-top/engine/originRegistry.js.map +0 -1
  86. package/lib/module/js-top/engine/patches/bridge.js.map +0 -1
  87. package/lib/module/js-top/engine/patches/microtasks.js.map +0 -1
  88. package/lib/module/js-top/engine/patches/promises.js.map +0 -1
  89. package/lib/module/js-top/engine/patches/timers.js.map +0 -1
  90. package/lib/module/js-top/engine/state.js.map +0 -1
  91. package/lib/module/js-top/engine/symbolicate.js.map +0 -1
  92. package/lib/module/js-top/sync/jsTopSyncAdapter.js.map +0 -1
  93. package/lib/module/js-top/types/index.js.map +0 -1
  94. package/lib/module/js-top/utils/preferences.js.map +0 -1
  95. package/lib/module/preset.js.map +0 -1
  96. package/lib/typescript/index.d.ts.map +0 -1
  97. package/lib/typescript/js-top/components/BusySparkline.d.ts.map +0 -1
  98. package/lib/typescript/js-top/components/JsTopHudOverlay.d.ts.map +0 -1
  99. package/lib/typescript/js-top/components/JsTopModal.d.ts.map +0 -1
  100. package/lib/typescript/js-top/components/TaskDetailView.d.ts.map +0 -1
  101. package/lib/typescript/js-top/components/TaskTable.d.ts.map +0 -1
  102. package/lib/typescript/js-top/engine/JsTopController.d.ts.map +0 -1
  103. package/lib/typescript/js-top/engine/aggregator.d.ts.map +0 -1
  104. package/lib/typescript/js-top/engine/busyProbe.d.ts.map +0 -1
  105. package/lib/typescript/js-top/engine/frameStack.d.ts.map +0 -1
  106. package/lib/typescript/js-top/engine/longtasks.d.ts.map +0 -1
  107. package/lib/typescript/js-top/engine/now.d.ts.map +0 -1
  108. package/lib/typescript/js-top/engine/originRegistry.d.ts.map +0 -1
  109. package/lib/typescript/js-top/engine/patches/bridge.d.ts.map +0 -1
  110. package/lib/typescript/js-top/engine/patches/microtasks.d.ts.map +0 -1
  111. package/lib/typescript/js-top/engine/patches/promises.d.ts.map +0 -1
  112. package/lib/typescript/js-top/engine/patches/timers.d.ts.map +0 -1
  113. package/lib/typescript/js-top/engine/state.d.ts.map +0 -1
  114. package/lib/typescript/js-top/engine/symbolicate.d.ts.map +0 -1
  115. package/lib/typescript/js-top/sync/jsTopSyncAdapter.d.ts.map +0 -1
  116. package/lib/typescript/js-top/types/index.d.ts.map +0 -1
  117. package/lib/typescript/js-top/utils/preferences.d.ts.map +0 -1
  118. package/lib/typescript/preset.d.ts.map +0 -1
@@ -1,126 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * wrapTask — the single wrapper every patched entry point uses, plus the
5
- * frame stack that makes nested wrapped calls book EXCLUSIVE time.
6
- *
7
- * The no-double-count rule: every wrapper at any depth books
8
- * `(end − start) − childrenMs` to its own origin and adds its TOTAL to its
9
- * parent frame's childrenMs. Thread occupancy is then the sum of exclusive
10
- * time at all depths, which equals the union of wrapped wall intervals —
11
- * nesting (e.g. rAF callbacks fired inside a wrapped bridge call-in) can
12
- * never inflate totals.
13
- *
14
- * Naming: exported/inner functions carry a `buoyJsTop` prefix on purpose —
15
- * Hermes dev stacks only show function names + bundle offsets, and the
16
- * registry's stack parser filters engine frames by that prefix.
17
- */
18
-
19
- import { now } from "./now";
20
- import { isActive, WRAPPED_TAG } from "./state";
21
- import { buoyJsTopBookExclusive, buoyJsTopOriginExists, buoyJsTopScheduleOrigin } from "./originRegistry";
22
- const DEPTH_CAP = 32;
23
- const stack = [];
24
-
25
- /** Depth-0 completed intervals ≥ this feed the longtask correlator. */
26
- const NOTABLE_INTERVAL_MS = 20;
27
- const INTERVAL_RING_SIZE = 64;
28
- const intervalRing = new Array(INTERVAL_RING_SIZE).fill(null);
29
- let intervalRingIndex = 0;
30
-
31
- /** Recent depth-0 wrapped intervals, unordered — consumers scan all slots. */
32
- export function recentIntervals() {
33
- return intervalRing;
34
- }
35
-
36
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
-
38
- /** Push a frame (unless depth-capped), run `invoke`, book on the way out. */
39
- function buoyJsTopRunFrame(originKey, invoke) {
40
- const start = now();
41
- const parent = stack.length > 0 ? stack[stack.length - 1] : null;
42
- let frame = null;
43
- if (stack.length < DEPTH_CAP) {
44
- frame = {
45
- originKey,
46
- start,
47
- childrenMs: 0
48
- };
49
- stack.push(frame);
50
- }
51
- try {
52
- // The user code runs OUTSIDE any catch of ours: errors propagate to the
53
- // runtime's normal unhandled-error path untouched.
54
- return invoke();
55
- } finally {
56
- try {
57
- if (frame) {
58
- stack.pop();
59
- const end = now();
60
- const total = end - start;
61
- const exclusive = Math.max(0, total - frame.childrenMs);
62
- if (parent) parent.childrenMs += total;
63
- buoyJsTopBookExclusive(originKey, exclusive, start, end);
64
- if (!parent && total >= NOTABLE_INTERVAL_MS) {
65
- intervalRing[intervalRingIndex] = {
66
- originKey,
67
- start,
68
- end
69
- };
70
- intervalRingIndex = (intervalRingIndex + 1) % INTERVAL_RING_SIZE;
71
- }
72
- }
73
- } catch {
74
- // Bookkeeping must never break the app.
75
- }
76
- }
77
- }
78
-
79
- /**
80
- * Wrap a callback at schedule time. ALWAYS wraps once patches are installed
81
- * (installation itself only happens on first engine start): a long-lived
82
- * callback — the classic forgotten setInterval — must stay attributable
83
- * even when it was scheduled while the engine was off, or after clear()
84
- * deleted its origin record. Identity is therefore resolved eagerly at
85
- * schedule time when possible (best stack context) and re-resolved lazily
86
- * at fire time otherwise. Inactive-engine cost per fire: one boolean.
87
- */
88
- export function buoyJsTopWrapTask(fn, api) {
89
- if (typeof fn !== "function") return fn;
90
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
- if (fn[WRAPPED_TAG]) return fn;
92
- let originKey = isActive() ? buoyJsTopScheduleOrigin(api, fn, now()) : null;
93
-
94
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
- const buoyJsTopTaskWrapper = function (...args) {
96
- if (!isActive()) return fn.apply(this, args);
97
- if (originKey === null || !buoyJsTopOriginExists(originKey)) {
98
- // Scheduled while the engine was off, or stats cleared since —
99
- // (re)resolve identity now, without stack capture (see scheduleOrigin).
100
- originKey = buoyJsTopScheduleOrigin(api, fn, now(), false);
101
- }
102
- return buoyJsTopRunFrame(originKey, () => fn.apply(this, args));
103
- };
104
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
- buoyJsTopTaskWrapper[WRAPPED_TAG] = true;
106
- return buoyJsTopTaskWrapper;
107
- }
108
-
109
- /**
110
- * Run a thunk as a named task frame — for entry points that aren't
111
- * schedule-a-callback shaped (e.g. bridge call-ins, where the "origin" is
112
- * the module.method being invoked right now).
113
- */
114
- export function buoyJsTopRunAs(api, name, thunk) {
115
- if (!isActive()) return thunk();
116
- const originKey = buoyJsTopScheduleOrigin(api, {
117
- name
118
- }, now());
119
- return buoyJsTopRunFrame(originKey, thunk);
120
- }
121
-
122
- /** Test-only: current nesting depth. */
123
- export function debugDepth() {
124
- return stack.length;
125
- }
126
- //# sourceMappingURL=frameStack.js.map
1
+ "use strict";import{now as s}from"./now";import{isActive as c,WRAPPED_TAG as f}from"./state";import{buoyJsTopBookExclusive as x,buoyJsTopOriginExists as A,buoyJsTopScheduleOrigin as p}from"./originRegistry";const b=32;const e=[];const g=20;const y=64;const T=new Array(y).fill(null);let a=0;export function recentIntervals(){return T}function h(t,i){const n=s();const r=e.length>0?e[e.length-1]:null;let o=null;if(e.length<b){o={originKey:t,start:n,childrenMs:0};e.push(o)}try{return i()}finally{try{if(o){e.pop();const u=s();const l=u-n;const m=Math.max(0,l-o.childrenMs);if(r)r.childrenMs+=l;x(t,m,n,u);if(!r&&l>=g){T[a]={originKey:t,start:n,end:u};a=(a+1)%y}}}catch{}}}export function buoyJsTopWrapTask(t,i){if(typeof t!=="function")return t;if(t[f])return t;let n=c()?p(i,t,s()):null;const r=function(...o){if(!c())return t.apply(this,o);if(n===null||!A(n)){n=p(i,t,s(),false)}return h(n,()=>t.apply(this,o))};r[f]=true;return r}export function buoyJsTopRunAs(t,i,n){if(!c())return n();const r=p(t,{name:i},s());return h(r,n)}export function debugDepth(){return e.length}
@@ -1,133 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Freeze tracking. RN ships a real `PerformanceObserver('longtask')` (C++
5
- * TurboModule, present in Expo Go and release): entries fire for any task
6
- * that blocks the JS event loop ≥ 50ms — what a user feels as a dropped tap
7
- * or stuttered animation — but carry NO attribution. We supply it: each
8
- * entry is overlap-matched against the ring of recent depth-0 wrapped
9
- * intervals; the biggest overlap names the offender.
10
- *
11
- * Freeze data doesn't get its own UI section — it decorates the places you
12
- * already look: per-row ⚠ badges in the task table and a summary chip in
13
- * the header, both fed by getFreezeStats() (trailing 60s window). The raw
14
- * entry list is still exported for the desktop panel and MCP payloads.
15
- *
16
- * The observer installs once and stays connected (uninstalling/reinstalling
17
- * observers has its own churn); its handler early-outs when the engine is
18
- * inactive — longtasks only fire on 50ms+ blocks, so the idle cost is nil.
19
- */
20
-
21
- import { g, isActive } from "./state";
22
- import { recentIntervals } from "./frameStack";
23
- import { labelForOrigin } from "./originRegistry";
24
- import { bookLongtaskRange } from "./aggregator";
25
- const MAX_ENTRIES = 40;
26
- /** Freeze badges/summary only consider the trailing window. */
27
- const FREEZE_WINDOW_MS = 60_000;
28
-
29
- /** Per-origin key for freezes we couldn't attribute to a wrapped callback. */
30
- export const SYSTEM_FREEZE_KEY = "unattributed";
31
- let installed = false;
32
- let supported = false;
33
- const entries = [];
34
- function correlate(start, duration) {
35
- const end = start + duration;
36
- let bestKey = null;
37
- let bestOverlap = 0;
38
- for (const interval of recentIntervals()) {
39
- if (!interval) continue;
40
- const overlap = Math.min(end, interval.end) - Math.max(start, interval.start);
41
- if (overlap > bestOverlap) {
42
- bestOverlap = overlap;
43
- bestKey = interval.originKey;
44
- }
45
- }
46
- return bestKey;
47
- }
48
- export function installLongtaskObserver() {
49
- if (installed) return {
50
- longtask: supported
51
- };
52
- installed = true;
53
- const PerformanceObserverCtor = g.PerformanceObserver;
54
- if (typeof PerformanceObserverCtor !== "function") {
55
- return {
56
- longtask: false
57
- };
58
- }
59
- try {
60
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
- const observer = new PerformanceObserverCtor(list => {
62
- if (!isActive()) return;
63
- try {
64
- for (const entry of list.getEntries()) {
65
- if (entry.entryType !== "longtask") continue;
66
- const start = entry.startTime;
67
- const duration = entry.duration;
68
- bookLongtaskRange(start, start + duration);
69
- entries.push({
70
- start,
71
- duration,
72
- at: Date.now(),
73
- originKey: correlate(start, duration)
74
- });
75
- if (entries.length > MAX_ENTRIES) entries.shift();
76
- }
77
- } catch {
78
- // Never let observation break the app.
79
- }
80
- });
81
- observer.observe({
82
- entryTypes: ["longtask"]
83
- });
84
- supported = true;
85
- } catch {
86
- supported = false;
87
- }
88
- return {
89
- longtask: supported
90
- };
91
- }
92
-
93
- /** Raw recent entries (desktop panel + MCP payload shape). */
94
- export function getLongtasks() {
95
- return entries.map(e => ({
96
- start: e.start,
97
- duration: e.duration,
98
- at: e.at,
99
- originLabel: e.originKey ? labelForOrigin(e.originKey) : null
100
- }));
101
- }
102
-
103
- /** Windowed per-origin freeze aggregation for badges + header summary. */
104
- export function getFreezeStats(now) {
105
- const byOrigin = {};
106
- let count = 0;
107
- let worstMs = 0;
108
- for (const e of entries) {
109
- if (e.at > 0 && now - e.at > FREEZE_WINDOW_MS) continue;
110
- count++;
111
- if (e.duration > worstMs) worstMs = e.duration;
112
- const key = e.originKey ?? SYSTEM_FREEZE_KEY;
113
- const s = byOrigin[key];
114
- if (s) {
115
- s.count++;
116
- if (e.duration > s.maxMs) s.maxMs = e.duration;
117
- } else {
118
- byOrigin[key] = {
119
- count: 1,
120
- maxMs: e.duration
121
- };
122
- }
123
- }
124
- return {
125
- count,
126
- worstMs,
127
- byOrigin
128
- };
129
- }
130
- export function clearLongtasks() {
131
- entries.length = 0;
132
- }
133
- //# sourceMappingURL=longtasks.js.map
1
+ "use strict";import{g as f,isActive as l}from"./state";import{recentIntervals as g}from"./frameStack";import{labelForOrigin as p}from"./originRegistry";import{bookLongtaskRange as m}from"./aggregator";const y=40;const d=6e4;export const SYSTEM_FREEZE_KEY="unattributed";let u=false;let c=false;const a=[];function b(n,o){const i=n+o;let r=null;let t=0;for(const e of g()){if(!e)continue;const s=Math.min(i,e.end)-Math.max(n,e.start);if(s>t){t=s;r=e.originKey}}return r}export function installLongtaskObserver(){if(u)return{longtask:c};u=true;const n=f.PerformanceObserver;if(typeof n!=="function"){return{longtask:false}}try{const o=new n(i=>{if(!l())return;try{for(const r of i.getEntries()){if(r.entryType!=="longtask")continue;const t=r.startTime;const e=r.duration;m(t,t+e);a.push({start:t,duration:e,at:Date.now(),originKey:b(t,e)});if(a.length>y)a.shift()}}catch{}});o.observe({entryTypes:["longtask"]});c=true}catch{c=false}return{longtask:c}}export function getLongtasks(){return a.map(n=>({start:n.start,duration:n.duration,at:n.at,originLabel:n.originKey?p(n.originKey):null}))}export function getFreezeStats(n){const o={};let i=0;let r=0;for(const t of a){if(t.at>0&&n-t.at>d)continue;i++;if(t.duration>r)r=t.duration;const e=t.originKey??SYSTEM_FREEZE_KEY;const s=o[e];if(s){s.count++;if(t.duration>s.maxMs)s.maxMs=t.duration}else{o[e]={count:1,maxMs:t.duration}}}return{count:i,worstMs:r,byOrigin:o}}export function clearLongtasks(){a.length=0}
@@ -1,33 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * High-res monotonic clock for the engine's hot path. Resolved once at module
5
- * load: `nativePerformanceNow` (JSI global, cheapest) → `performance.now` →
6
- * `Date.now`. All engine timestamps share this single time base.
7
- */
8
-
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- const g =
11
- // eslint-disable-next-line no-restricted-globals
12
- typeof globalThis !== "undefined" ? globalThis : undefined;
13
- const resolved = (() => {
14
- if (typeof g?.nativePerformanceNow === "function") {
15
- const native = g.nativePerformanceNow;
16
- return () => native();
17
- }
18
- if (typeof g?.performance?.now === "function") {
19
- const perf = g.performance;
20
- return () => perf.now();
21
- }
22
- return () => Date.now();
23
- })();
24
- let clock = resolved;
25
- export function now() {
26
- return clock();
27
- }
28
-
29
- /** Test-only: swap the engine clock for a controllable one. */
30
- export function setNowForTests(fn) {
31
- clock = fn ?? resolved;
32
- }
33
- //# sourceMappingURL=now.js.map
1
+ "use strict";const n=typeof globalThis!=="undefined"?globalThis:void 0;const o=(()=>{if(typeof n?.nativePerformanceNow==="function"){const e=n.nativePerformanceNow;return()=>e()}if(typeof n?.performance?.now==="function"){const e=n.performance;return()=>e.now()}return()=>Date.now()})();let t=o;export function now(){return t()}export function setNowForTests(e){t=e??o}