catalyst-core 0.2.0-beta.2 → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.native.config.cjs +8 -0
- package/bin/catalyst.js +49 -105
- package/changelog.md +8 -0
- package/dist/caching.js +1 -1
- package/dist/index.jsx +8 -0
- package/dist/logger.js +74 -3
- package/dist/native/androidProject/app/build.gradle.kts +1 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
- package/dist/native/androidSetup.js +300 -8
- package/dist/native/bridge/WebBridge.js +481 -59
- package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
- package/dist/native/bridge/errors.js +250 -26
- package/dist/native/bridge/hooks/index.js +125 -1
- package/dist/native/bridge/hooks/useCamera.js +347 -9
- package/dist/native/bridge/hooks/useDataProtection.js +200 -2
- package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
- package/dist/native/bridge/hooks/useFilePicker.js +429 -2
- package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
- package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
- package/dist/native/bridge/hooks/useIntent.js +95 -1
- package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
- package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
- package/dist/native/bridge/hooks/useNotification.js +318 -2
- package/dist/native/bridge/hooks/useSafeArea.js +84 -2
- package/dist/native/bridge/hooks/useVideoStream.js +434 -6
- package/dist/native/bridge/hooks/utils.js +27 -1
- package/dist/native/bridge/hooks.js +16 -1
- package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
- package/dist/native/bridge/perf/core/constants.js +126 -0
- package/dist/native/bridge/perf/core/measure.js +189 -0
- package/dist/native/bridge/perf/core/session.js +100 -0
- package/dist/native/bridge/perf/core/store.js +554 -0
- package/dist/native/bridge/perf/core/summary.js +174 -0
- package/dist/native/bridge/perf/core/traceExport.js +240 -0
- package/dist/native/bridge/perf/core/waterfalls.js +146 -0
- package/dist/native/bridge/perf/index.js +402 -0
- package/dist/native/bridge/safeArea.js +53 -5
- package/dist/native/bridge/standardizedHooks.js +595 -82
- package/dist/native/bridge/useBaseHook.js +274 -38
- package/dist/native/bridge/utils/CameraUtils.js +201 -56
- package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
- package/dist/native/bridge/utils/NativeBridge.js +425 -141
- package/dist/native/buildAndroid/assets.js +510 -13
- package/dist/native/buildAndroid/build.js +296 -19
- package/dist/native/buildAndroid/config.js +87 -1
- package/dist/native/buildAndroid/index.js +374 -1
- package/dist/native/buildAppAndroid.js +34 -2
- package/dist/native/buildAppIos.js +62 -2
- package/dist/native/buildIos/assets.js +609 -8
- package/dist/native/buildIos/build.js +639 -20
- package/dist/native/buildIos/config.js +294 -12
- package/dist/native/buildIos/index.js +343 -6
- package/dist/native/buildIos/plugins.js +343 -16
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
- package/dist/native/internalPluginUtils.js +232 -7
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
- package/dist/native/package.json +3 -0
- package/dist/native/plugin-bridge/PluginBridge.js +227 -1
- package/dist/native/pluginComposerAndroid.js +407 -23
- package/dist/native/pluginComposerIos.js +435 -11
- package/dist/native/renameAndroidProject.js +1094 -88
- package/dist/native/setupEmulatorIos.js +289 -22
- package/dist/native/setupServer.js +160 -19
- package/dist/native/terminalProgress.js +199 -11
- package/dist/native/utils.js +193 -15
- package/dist/offline/catalyst-sw.js +145 -1
- package/dist/offline/registerServiceWorker.js +33 -1
- package/dist/otel.js +375 -46
- package/dist/router/ClientRouter.js +14 -2
- package/dist/router/ServerRouter.js +9 -1
- package/dist/router.js +1 -1
- package/dist/scripts/build.js +112 -3
- package/dist/scripts/checkVersion.js +3 -1
- package/dist/scripts/cleanCache.js +30 -1
- package/dist/scripts/generateOfflineManifest.js +116 -1
- package/dist/scripts/loadEnvironmentVariables.js +32 -6
- package/dist/scripts/plugins.js +309 -1
- package/dist/scripts/preServerInit.js +13 -0
- package/dist/scripts/scriptUtils.js +54 -3
- package/dist/scripts/serve.js +51 -4
- package/dist/scripts/start.js +47 -10
- package/dist/scripts/validator.js +149 -1
- package/dist/sentry.cjs +127 -0
- package/dist/sentry.js +125 -5
- package/dist/server/expressServer.js +250 -5
- package/dist/server/manifestCache.js +36 -0
- package/dist/server/renderer/ChunkExtractor.js +144 -0
- package/dist/server/renderer/document/Body.jsx +59 -0
- package/dist/server/renderer/document/FastRefresh.jsx +20 -0
- package/dist/server/renderer/document/Head.jsx +60 -0
- package/dist/server/renderer/document/index.jsx +2 -0
- package/dist/server/renderer/extract.js +149 -31
- package/dist/server/renderer/handler.jsx +433 -0
- package/dist/server/renderer/index.js +8 -1
- package/dist/server/renderer/render.js +47 -0
- package/dist/server/utils/botDetectionMiddleware.js +29 -0
- package/dist/server/utils/cjsRequire.js +10 -0
- package/dist/server/utils/userAgentUtil.js +87 -9
- package/dist/server/utils/validator.js +164 -1
- package/dist/vite/FastRefresh.jsx +20 -0
- package/dist/vite/inject-cache-key-plugin.js +276 -0
- package/dist/vite/loadCustomViteConfig.js +22 -0
- package/dist/vite/manifest-categorization-plugin.js +455 -0
- package/dist/vite/node-loader.mjs +458 -0
- package/dist/vite/scssParams.js +21 -0
- package/dist/vite/vite.config.client.js +130 -0
- package/dist/vite/vite.config.js +422 -0
- package/dist/vite/vite.config.server.js +84 -0
- package/dist/web-router/components/MetaTag.jsx +34 -0
- package/dist/web-router/components/RouterDataProvider.jsx +332 -0
- package/dist/web-router/components/Split.jsx +200 -0
- package/dist/web-router/components/SplitInview.jsx +106 -0
- package/dist/web-router/components/SsrRequestContext.jsx +14 -0
- package/dist/web-router/context.jsx +12 -0
- package/dist/web-router/hooks.jsx +72 -0
- package/dist/web-router/types/components/MetaTag.d.ts +2 -0
- package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
- package/dist/web-router/types/context.d.ts +14 -0
- package/dist/web-router/types/hooks.d.ts +1 -0
- package/dist/web-router/types/index.d.ts +3 -0
- package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +119 -0
- package/mcp_v2/conversion-tasks.json +35 -17
- package/mcp_v2/knowledge-base.json +124 -13
- package/mcp_v2/lib/helpers.js +10 -0
- package/mcp_v2/mcp.js +4 -4
- package/mcp_v2/package.json +1 -0
- package/mcp_v2/tools/build.js +116 -20
- package/mcp_v2/tools/conversion.js +106 -5
- package/mcp_v2/tools/tasks.js +18 -14
- package/package.json +35 -68
- package/babel.config.js +0 -32
- package/dist/index.js +0 -1
- package/dist/scripts/devBuild.js +0 -3
- package/dist/scripts/devServe.js +0 -3
- package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
- package/dist/scripts/registerAliases.js +0 -6
- package/dist/server/renderer/document/Body.js +0 -11
- package/dist/server/renderer/document/Head.js +0 -9
- package/dist/server/renderer/document/index.js +0 -1
- package/dist/server/renderer/handler.js +0 -47
- package/dist/server/startServer.js +0 -57
- package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
- package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
- package/dist/webpack/babel.config.client.js +0 -1
- package/dist/webpack/babel.config.ssr.js +0 -1
- package/dist/webpack/base.babel.js +0 -7
- package/dist/webpack/development.client.babel.js +0 -10
- package/dist/webpack/production.client.babel.js +0 -4
- package/dist/webpack/production.ssr.babel.js +0 -8
- package/dist/webpack/scssParams.js +0 -9
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TRACK_GROUP = exports.TRACK = exports.THRESHOLD = exports.PREFIX = exports.COLOR = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* constants.js
|
|
9
|
+
*
|
|
10
|
+
* Single source of truth for track names, thresholds, and color rules.
|
|
11
|
+
* All collectors import from here — no magic strings scattered across files.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ─── Chrome DevTools track group ─────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const TRACK_GROUP = exports.TRACK_GROUP = "Catalyst";
|
|
17
|
+
const TRACK = exports.TRACK = {
|
|
18
|
+
NAVIGATION: "Navigation",
|
|
19
|
+
SCROLL: "Scroll",
|
|
20
|
+
INPUT: "Input",
|
|
21
|
+
KEYBOARD: "Keyboard",
|
|
22
|
+
// alias kept for backward compat — collectors use TRACK.INPUT
|
|
23
|
+
RENDER: "Render",
|
|
24
|
+
MEMORY: "Memory",
|
|
25
|
+
CACHE: "Cache",
|
|
26
|
+
NETWORK: "Network",
|
|
27
|
+
HOOK: "Hook",
|
|
28
|
+
INTERACTION: "Interaction",
|
|
29
|
+
INSIGHTS: "Insights",
|
|
30
|
+
NATIVE_API: "Native API"
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ─── Measure name prefixes ────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
const PREFIX = exports.PREFIX = {
|
|
36
|
+
SESSION_NAV: "catalyst:session/navigation",
|
|
37
|
+
SESSION_SCROLL: "catalyst:session/scroll",
|
|
38
|
+
SESSION_INPUT: "catalyst:session/input",
|
|
39
|
+
SESSION_KEYBOARD: "catalyst:session/keyboard",
|
|
40
|
+
// alias — KeyboardCollector still uses this
|
|
41
|
+
LCP: "catalyst:lcp",
|
|
42
|
+
FCP: "catalyst:fcp",
|
|
43
|
+
LOAF: "catalyst:loaf",
|
|
44
|
+
LAYOUT_SHIFT: "catalyst:layout-shift",
|
|
45
|
+
VIEWPORT_RESIZE: "catalyst:viewport-resize",
|
|
46
|
+
ROUTE_TRANSITION: "catalyst:route-transition",
|
|
47
|
+
CACHE: "catalyst:cache",
|
|
48
|
+
CACHE_GROUP: "catalyst:cache-group",
|
|
49
|
+
NETWORK_TIMING: "catalyst:network",
|
|
50
|
+
NATIVE_API: "catalyst:native-api",
|
|
51
|
+
HOOK_MOUNT: "catalyst:hook/mount",
|
|
52
|
+
HOOK_LAZY: "catalyst:hook/lazy",
|
|
53
|
+
INTERACTION: "catalyst:interaction",
|
|
54
|
+
SESSION_INTERACTION: "catalyst:session/interaction",
|
|
55
|
+
LONG_PRESS: "catalyst:long-press",
|
|
56
|
+
INSIGHT: "catalyst:insight",
|
|
57
|
+
HW_ACCEL: "catalyst:hw-accel",
|
|
58
|
+
// Native MetricsMonitor events
|
|
59
|
+
PAGE_LOAD: "catalyst:page-load",
|
|
60
|
+
COLD_START: "catalyst:cold-start",
|
|
61
|
+
FPS_DROP: "catalyst:fps-drop",
|
|
62
|
+
LONG_TASK: "catalyst:long-task",
|
|
63
|
+
MEMORY_JVM: "catalyst:mem/jvm",
|
|
64
|
+
MEMORY_NATIVE: "catalyst:mem/native",
|
|
65
|
+
MEMORY_TOTAL: "catalyst:mem/total"
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// ─── Thresholds ───────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
const THRESHOLD = exports.THRESHOLD = {
|
|
71
|
+
LCP_BAD_MS: 2500,
|
|
72
|
+
// LCP > 2.5s = poor (Core Web Vitals)
|
|
73
|
+
NAV_SESSION_BAD_MS: 2500,
|
|
74
|
+
// navigation session > 2.5s = slow page
|
|
75
|
+
FETCH_SLOW_MS: 300,
|
|
76
|
+
// network fetch > 300ms = slow
|
|
77
|
+
SCROLL_IDLE_MS: 150,
|
|
78
|
+
// scroll ends after 150ms of no scroll events
|
|
79
|
+
VIEWPORT_MIN_DELTA: 50,
|
|
80
|
+
// ignore viewport resize < 50px (not keyboard)
|
|
81
|
+
CACHE_GROUP_IDLE_MS: 300,
|
|
82
|
+
// close cache group after 300ms idle
|
|
83
|
+
LOAF_WINDOW: 5,
|
|
84
|
+
// keep last N LoAF entries for overlap detection
|
|
85
|
+
LONG_PRESS_MS: 500,
|
|
86
|
+
// pointerdown held > 500ms = long press
|
|
87
|
+
INTERACTION_SLOW_MS: 100,
|
|
88
|
+
// tap → paint > 100ms = slow response
|
|
89
|
+
LAZY_LOAF_WINDOW_MS: 2000,
|
|
90
|
+
// watch for LoAF bursts up to 2s after interaction
|
|
91
|
+
CLS_BAD: 0.1 // CLS > 0.1 per entry = significant shift
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ─── Colors ───────────────────────────────────────────────────────────────────
|
|
95
|
+
// Chrome DevTools Extensibility API color values (all 9 values):
|
|
96
|
+
//
|
|
97
|
+
// primary / primary-light / primary-dark → blue tones
|
|
98
|
+
// secondary / secondary-light / secondary-dark → purple/grey tones
|
|
99
|
+
// tertiary / tertiary-light / tertiary-dark → yellow/orange tones
|
|
100
|
+
// error → red
|
|
101
|
+
//
|
|
102
|
+
// Usage guide:
|
|
103
|
+
// error = bad outcome (LoAF, miss, slow native API, memory critical)
|
|
104
|
+
// primary = cache HIT memory (fastest path)
|
|
105
|
+
// primary-light = cache HIT disk (good, slightly slower)
|
|
106
|
+
// primary-dark = good LCP, clean scroll session
|
|
107
|
+
// secondary = informational (native API, keyboard, viewport, mem/total normal)
|
|
108
|
+
// primary = mem/jvm (JVM heap — your Kotlin/Java code)
|
|
109
|
+
// tertiary-dark = mem/native (V8 + Blink + JNI — WebView proxy; watch for growth)
|
|
110
|
+
// secondary-light = route transition, hook mount (neutral)
|
|
111
|
+
// tertiary = network fetch after miss (expected but slow)
|
|
112
|
+
// tertiary-light = small CLS, long-press (low signal)
|
|
113
|
+
// tertiary-dark = insight warning (non-critical)
|
|
114
|
+
|
|
115
|
+
const COLOR = exports.COLOR = {
|
|
116
|
+
ERROR: "error",
|
|
117
|
+
PRIMARY: "primary",
|
|
118
|
+
PRIMARY_LIGHT: "primary-light",
|
|
119
|
+
PRIMARY_DARK: "primary-dark",
|
|
120
|
+
SECONDARY: "secondary",
|
|
121
|
+
SECONDARY_LIGHT: "secondary-light",
|
|
122
|
+
SECONDARY_DARK: "secondary-dark",
|
|
123
|
+
TERTIARY: "tertiary",
|
|
124
|
+
TERTIARY_LIGHT: "tertiary-light",
|
|
125
|
+
TERTIARY_DARK: "tertiary-dark"
|
|
126
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Measure = void 0;
|
|
7
|
+
var _constants = require("./constants.js");
|
|
8
|
+
/**
|
|
9
|
+
* measure.js
|
|
10
|
+
*
|
|
11
|
+
* Shared _measure() and _colorFor() logic used by every collector.
|
|
12
|
+
* Injected as a dependency — collectors never call performance.measure() directly.
|
|
13
|
+
*
|
|
14
|
+
* Chrome DevTools Extensibility API rules (Chrome 128+):
|
|
15
|
+
* - detail must be { devtools: { dataType:'track-entry', ... } }
|
|
16
|
+
* - custom data goes in properties[] as [key, value] pairs
|
|
17
|
+
* - start AND end must both be numeric ms timestamps — passing a mark name
|
|
18
|
+
* string as start causes silent fallback to Timings row (confirmed Chrome 145)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const INTERNAL_PROPERTIES = new Set(["simulatorValid"]);
|
|
22
|
+
const PROPERTY_LABELS = {
|
|
23
|
+
interactionId: "Related tap",
|
|
24
|
+
parentOperation: "Parent operation",
|
|
25
|
+
callId: "Call ID",
|
|
26
|
+
requestId: "Request ID",
|
|
27
|
+
memoryId: "Memory sample"
|
|
28
|
+
};
|
|
29
|
+
const PROPERTY_PRIORITY = {
|
|
30
|
+
interactionId: 0,
|
|
31
|
+
parentOperation: 1,
|
|
32
|
+
outcome: 2,
|
|
33
|
+
ok: 3,
|
|
34
|
+
status: 4,
|
|
35
|
+
durationMs: 5,
|
|
36
|
+
roundTripMs: 5,
|
|
37
|
+
responseMs: 5
|
|
38
|
+
};
|
|
39
|
+
class Measure {
|
|
40
|
+
constructor() {
|
|
41
|
+
// Rolling window of recent LoAF entries — shared across all collectors
|
|
42
|
+
// so any collector can check loafOverlap without knowing about others.
|
|
43
|
+
this._recentLoafs = [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Record a LoAF entry so overlap detection stays current.
|
|
48
|
+
* Called by RenderCollector whenever a long-animation-frame fires.
|
|
49
|
+
*/
|
|
50
|
+
recordLoaf(start, end) {
|
|
51
|
+
this._recentLoafs.push({
|
|
52
|
+
start,
|
|
53
|
+
end
|
|
54
|
+
});
|
|
55
|
+
if (this._recentLoafs.length > _constants.THRESHOLD.LOAF_WINDOW) {
|
|
56
|
+
this._recentLoafs.shift();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
overlapsLoaf(startMs, endMs) {
|
|
60
|
+
return this._recentLoafs.some(loaf => startMs < loaf.end && endMs > loaf.start);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Emit a performance.measure() with Chrome DevTools Extensibility API metadata.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} name - measure name (catalyst:...)
|
|
67
|
+
* @param {string} startMark - name of the opening performance.mark()
|
|
68
|
+
* @param {number} endTime - end time in ms (performance.now()-space)
|
|
69
|
+
* @param {object} detail - arbitrary detail fields added as properties[]
|
|
70
|
+
* @param {string} track - Chrome DevTools track name (from TRACK constant)
|
|
71
|
+
* @param {string} [color] - override color; if omitted, derived from name+detail
|
|
72
|
+
* @param {string} [trackGroup] - Chrome DevTools track group
|
|
73
|
+
*/
|
|
74
|
+
emit(name, startMark, endTime, detail = {}, track, color = null, trackGroup = _constants.TRACK_GROUP) {
|
|
75
|
+
try {
|
|
76
|
+
const startTime = performance.getEntriesByName(startMark, "mark")[0]?.startTime;
|
|
77
|
+
if (startTime == null) {
|
|
78
|
+
console.warn("[CatalystPerf] measure skipped — mark not found:", startMark);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const resolvedColor = color ?? this._colorFor(name, detail, startTime, endTime);
|
|
82
|
+
const properties = this._properties(detail);
|
|
83
|
+
performance.measure(name, {
|
|
84
|
+
start: startTime,
|
|
85
|
+
end: endTime,
|
|
86
|
+
detail: {
|
|
87
|
+
devtools: {
|
|
88
|
+
dataType: "track-entry",
|
|
89
|
+
trackGroup,
|
|
90
|
+
track,
|
|
91
|
+
color: resolvedColor,
|
|
92
|
+
tooltipText: name,
|
|
93
|
+
properties
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.warn("[CatalystPerf] measure failed:", name, e.message);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
marker(name, startTime, detail = {}, color = _constants.COLOR.SECONDARY) {
|
|
102
|
+
try {
|
|
103
|
+
performance.mark(name, {
|
|
104
|
+
startTime,
|
|
105
|
+
detail: {
|
|
106
|
+
devtools: {
|
|
107
|
+
dataType: "marker",
|
|
108
|
+
color,
|
|
109
|
+
tooltipText: name,
|
|
110
|
+
properties: this._properties(detail)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.warn("[CatalystPerf] marker failed:", name, e.message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
_properties(detail) {
|
|
119
|
+
return Object.entries(detail).filter(([key, value]) => !INTERNAL_PROPERTIES.has(key) && value != null).sort(([left], [right]) => (PROPERTY_PRIORITY[left] ?? 100) - (PROPERTY_PRIORITY[right] ?? 100)).map(([key, value]) => [this._propertyName(key), this._propertyValue(value)]);
|
|
120
|
+
}
|
|
121
|
+
_propertyName(key) {
|
|
122
|
+
if (PROPERTY_LABELS[key]) return PROPERTY_LABELS[key];
|
|
123
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._-]+/g, " ").replace(/^./, char => char.toUpperCase());
|
|
124
|
+
}
|
|
125
|
+
_propertyValue(value) {
|
|
126
|
+
if (typeof value === "object") {
|
|
127
|
+
try {
|
|
128
|
+
return JSON.stringify(value);
|
|
129
|
+
} catch {
|
|
130
|
+
return String(value);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return String(value);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Color derivation ─────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
_colorFor(name, detail, startTime, endTime) {
|
|
139
|
+
const duration = endTime - startTime;
|
|
140
|
+
|
|
141
|
+
// LoAF — always error (it's a bad frame by definition)
|
|
142
|
+
if (name.startsWith(_constants.PREFIX.LOAF)) return _constants.COLOR.ERROR;
|
|
143
|
+
|
|
144
|
+
// LCP — error if slow
|
|
145
|
+
if (name.startsWith(_constants.PREFIX.LCP)) return detail.renderTime > _constants.THRESHOLD.LCP_BAD_MS ? _constants.COLOR.ERROR : _constants.COLOR.PRIMARY;
|
|
146
|
+
if (name.startsWith(_constants.PREFIX.FCP)) return _constants.COLOR.PRIMARY;
|
|
147
|
+
|
|
148
|
+
// Navigation session
|
|
149
|
+
if (name.startsWith(_constants.PREFIX.SESSION_NAV)) return duration > _constants.THRESHOLD.NAV_SESSION_BAD_MS ? _constants.COLOR.ERROR : _constants.COLOR.PRIMARY;
|
|
150
|
+
|
|
151
|
+
// Route transition
|
|
152
|
+
if (name.startsWith(_constants.PREFIX.ROUTE_TRANSITION)) return _constants.COLOR.SECONDARY;
|
|
153
|
+
|
|
154
|
+
// Scroll session — error if janky
|
|
155
|
+
if (name.startsWith(_constants.PREFIX.SESSION_SCROLL)) return detail.loafCount > 0 ? _constants.COLOR.ERROR : _constants.COLOR.PRIMARY;
|
|
156
|
+
|
|
157
|
+
// Keyboard / viewport — informational
|
|
158
|
+
if (name.startsWith(_constants.PREFIX.SESSION_KEYBOARD)) return _constants.COLOR.SECONDARY;
|
|
159
|
+
if (name.startsWith(_constants.PREFIX.VIEWPORT_RESIZE)) return _constants.COLOR.SECONDARY;
|
|
160
|
+
|
|
161
|
+
// Layout shift
|
|
162
|
+
if (name.startsWith(_constants.PREFIX.LAYOUT_SHIFT)) return detail.value > _constants.THRESHOLD.CLS_BAD ? _constants.COLOR.ERROR : _constants.COLOR.TERTIARY;
|
|
163
|
+
|
|
164
|
+
// Cache spans — color passed explicitly by CacheCollector, this is fallback only
|
|
165
|
+
if (name.startsWith(_constants.PREFIX.CACHE + "/")) {
|
|
166
|
+
if (name.includes("/hit|memory|")) return _constants.COLOR.PRIMARY;
|
|
167
|
+
if (name.includes("/hit|disk|")) return _constants.COLOR.PRIMARY_LIGHT;
|
|
168
|
+
if (name.includes("/miss|")) return _constants.COLOR.ERROR;
|
|
169
|
+
return _constants.COLOR.TERTIARY;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Browser network calls
|
|
173
|
+
if (name.startsWith(_constants.PREFIX.NETWORK_TIMING)) {
|
|
174
|
+
if (detail.ok === false || duration > _constants.THRESHOLD.FETCH_SLOW_MS) return _constants.COLOR.ERROR;
|
|
175
|
+
return _constants.COLOR.SECONDARY;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Hook spans — error if LoAF burst detected during mount
|
|
179
|
+
if (name.startsWith(_constants.PREFIX.HOOK_MOUNT) || name.startsWith(_constants.PREFIX.HOOK_LAZY)) return detail.loafCount > 0 ? _constants.COLOR.ERROR : _constants.COLOR.SECONDARY;
|
|
180
|
+
|
|
181
|
+
// Interaction
|
|
182
|
+
if (name.startsWith(_constants.PREFIX.INTERACTION) || Number.isFinite(detail.responseMs)) return (detail.responseMs ?? duration) > _constants.THRESHOLD.INTERACTION_SLOW_MS ? _constants.COLOR.ERROR : _constants.COLOR.PRIMARY;
|
|
183
|
+
|
|
184
|
+
// Long press — just informational
|
|
185
|
+
if (name.startsWith(_constants.PREFIX.LONG_PRESS)) return _constants.COLOR.TERTIARY;
|
|
186
|
+
return _constants.COLOR.SECONDARY;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.Measure = Measure;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Session = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* session.js
|
|
9
|
+
*
|
|
10
|
+
* Reusable open/close session pattern.
|
|
11
|
+
* A session is a named span that stays open until explicitly closed.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const session = new Session('catalyst:session/scroll', measure, TRACK.SCROLL)
|
|
15
|
+
* session.open({ loafCount: 0 })
|
|
16
|
+
* session.close({ loafCount: 3, janky: true })
|
|
17
|
+
*
|
|
18
|
+
* The session owns its own start mark so callers don't track mark names.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class Session {
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} name - base measure name (catalyst:session/...)
|
|
24
|
+
* @param {Measure} measure - shared Measure instance
|
|
25
|
+
* @param {string} track - Chrome DevTools track name
|
|
26
|
+
*/
|
|
27
|
+
constructor(name, measure, track) {
|
|
28
|
+
this._name = name;
|
|
29
|
+
this._measure = measure;
|
|
30
|
+
this._track = track;
|
|
31
|
+
this._state = null; // { startMark, startTime, ...extra } | null
|
|
32
|
+
}
|
|
33
|
+
get isOpen() {
|
|
34
|
+
return this._state !== null;
|
|
35
|
+
}
|
|
36
|
+
get startTime() {
|
|
37
|
+
return this._state?.startTime ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Open the session.
|
|
42
|
+
* @param {object} extra - any fields to carry in state (e.g. loafCount)
|
|
43
|
+
* @param {number} [atTime] - override start time (default: performance.now())
|
|
44
|
+
*/
|
|
45
|
+
open(extra = {}, atTime = null) {
|
|
46
|
+
if (this._state) return; // already open — ignore double-open
|
|
47
|
+
const startTime = atTime ?? performance.now();
|
|
48
|
+
const startMark = `${this._name}:start:${Math.round(startTime)}`;
|
|
49
|
+
performance.mark(startMark, {
|
|
50
|
+
startTime
|
|
51
|
+
});
|
|
52
|
+
this._state = {
|
|
53
|
+
startMark,
|
|
54
|
+
startTime,
|
|
55
|
+
...extra
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Close the session and emit the measure.
|
|
61
|
+
* @param {object} detail - fields added to properties[] in DevTools
|
|
62
|
+
* @param {number} [atTime] - override end time (default: performance.now())
|
|
63
|
+
* @param {string} [color] - override color
|
|
64
|
+
*/
|
|
65
|
+
close(detail = {}, atTime = null, color = null) {
|
|
66
|
+
if (!this._state) return;
|
|
67
|
+
const {
|
|
68
|
+
startMark,
|
|
69
|
+
startTime
|
|
70
|
+
} = this._state;
|
|
71
|
+
const endTime = atTime ?? performance.now();
|
|
72
|
+
this._state = null;
|
|
73
|
+
this._measure.emit(this._name, startMark, endTime, {
|
|
74
|
+
duration: Math.round(endTime - startTime),
|
|
75
|
+
...detail
|
|
76
|
+
}, this._track, color);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Read a value stored in session state (e.g. loafCount).
|
|
81
|
+
*/
|
|
82
|
+
get(key) {
|
|
83
|
+
return this._state?.[key];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Update a value in session state without closing.
|
|
88
|
+
*/
|
|
89
|
+
set(key, value) {
|
|
90
|
+
if (this._state) this._state[key] = value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Increment a numeric counter in session state.
|
|
95
|
+
*/
|
|
96
|
+
increment(key) {
|
|
97
|
+
if (this._state) this._state[key] = (this._state[key] ?? 0) + 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.Session = Session;
|