catalyst-core 0.2.0-beta.3 → 0.3.0-beta.2

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 (167) hide show
  1. package/babel.native.config.cjs +8 -0
  2. package/bin/catalyst.js +49 -105
  3. package/changelog.md +8 -0
  4. package/dist/caching.js +1 -1
  5. package/dist/globals.d.ts +7 -0
  6. package/dist/index.jsx +8 -0
  7. package/dist/logger.js +74 -3
  8. package/dist/native/androidProject/app/build.gradle.kts +45 -4
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +86 -0
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +8 -0
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +173 -13
  12. package/dist/native/androidProject/settings.gradle.kts +10 -0
  13. package/dist/native/androidSetup.js +300 -8
  14. package/dist/native/bridge/WebBridge.js +481 -59
  15. package/dist/native/bridge/constants/NativeInterfaces.js +304 -45
  16. package/dist/native/bridge/errors.js +250 -26
  17. package/dist/native/bridge/hooks/index.js +132 -1
  18. package/dist/native/bridge/hooks/useAI.js +94 -0
  19. package/dist/native/bridge/hooks/useCamera.js +347 -9
  20. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  21. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  22. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  23. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  24. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  25. package/dist/native/bridge/hooks/useIntent.js +95 -1
  26. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  27. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  28. package/dist/native/bridge/hooks/useNotification.js +318 -2
  29. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  30. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  31. package/dist/native/bridge/hooks/utils.js +27 -1
  32. package/dist/native/bridge/hooks.js +16 -1
  33. package/dist/native/bridge/perf/collectors/ApiCollector.js +66 -3
  34. package/dist/native/bridge/perf/collectors/CacheCollector.js +186 -11
  35. package/dist/native/bridge/perf/collectors/HookCollector.js +141 -19
  36. package/dist/native/bridge/perf/collectors/InsightsCollector.js +481 -23
  37. package/dist/native/bridge/perf/collectors/InteractionCollector.js +245 -10
  38. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +107 -8
  39. package/dist/native/bridge/perf/collectors/NavigationCollector.js +273 -32
  40. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +178 -2
  41. package/dist/native/bridge/perf/collectors/RenderCollector.js +258 -14
  42. package/dist/native/bridge/perf/collectors/ScrollCollector.js +95 -9
  43. package/dist/native/bridge/perf/collectors/ThreadCollector.js +90 -6
  44. package/dist/native/bridge/perf/core/constants.js +103 -20
  45. package/dist/native/bridge/perf/core/measure.js +179 -30
  46. package/dist/native/bridge/perf/core/session.js +89 -23
  47. package/dist/native/bridge/perf/core/store.js +554 -1
  48. package/dist/native/bridge/perf/core/summary.js +174 -1
  49. package/dist/native/bridge/perf/core/traceExport.js +240 -1
  50. package/dist/native/bridge/perf/core/waterfalls.js +146 -1
  51. package/dist/native/bridge/perf/index.js +359 -38
  52. package/dist/native/bridge/safeArea.js +53 -5
  53. package/dist/native/bridge/standardizedHooks.js +595 -82
  54. package/dist/native/bridge/useBaseHook.js +274 -38
  55. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  56. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  57. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  58. package/dist/native/buildAndroid/assets.js +510 -13
  59. package/dist/native/buildAndroid/build.js +296 -19
  60. package/dist/native/buildAndroid/config.js +87 -1
  61. package/dist/native/buildAndroid/index.js +406 -1
  62. package/dist/native/buildAppAndroid.js +34 -2
  63. package/dist/native/buildAppIos.js +62 -2
  64. package/dist/native/buildIos/assets.js +609 -8
  65. package/dist/native/buildIos/build.js +639 -20
  66. package/dist/native/buildIos/config.js +294 -12
  67. package/dist/native/buildIos/index.js +343 -6
  68. package/dist/native/buildIos/plugins.js +343 -16
  69. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  70. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  71. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  72. package/dist/native/internalPluginUtils.js +232 -7
  73. package/dist/native/package.json +3 -0
  74. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  75. package/dist/native/pluginComposerAndroid.js +407 -23
  76. package/dist/native/pluginComposerIos.js +435 -11
  77. package/dist/native/renameAndroidProject.js +1094 -88
  78. package/dist/native/setupEmulatorIos.js +289 -22
  79. package/dist/native/setupServer.js +160 -19
  80. package/dist/native/terminalProgress.js +199 -11
  81. package/dist/native/utils.js +193 -15
  82. package/dist/offline/catalyst-sw.js +145 -1
  83. package/dist/offline/registerServiceWorker.js +33 -1
  84. package/dist/otel.js +375 -46
  85. package/dist/router/ClientRouter.js +14 -2
  86. package/dist/router/ServerRouter.js +9 -1
  87. package/dist/router.js +1 -1
  88. package/dist/scripts/build.js +112 -3
  89. package/dist/scripts/checkVersion.js +3 -1
  90. package/dist/scripts/cleanCache.js +30 -1
  91. package/dist/scripts/generateOfflineManifest.js +116 -1
  92. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  93. package/dist/scripts/plugins.js +309 -1
  94. package/dist/scripts/preServerInit.js +13 -0
  95. package/dist/scripts/scriptUtils.js +54 -3
  96. package/dist/scripts/serve.js +51 -4
  97. package/dist/scripts/start.js +47 -10
  98. package/dist/scripts/validator.js +149 -1
  99. package/dist/sentry.cjs +127 -0
  100. package/dist/sentry.js +125 -5
  101. package/dist/server/expressServer.js +296 -5
  102. package/dist/server/manifestCache.js +36 -0
  103. package/dist/server/renderer/ChunkExtractor.js +144 -0
  104. package/dist/server/renderer/document/Body.jsx +59 -0
  105. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  106. package/dist/server/renderer/document/Head.jsx +60 -0
  107. package/dist/server/renderer/document/index.jsx +2 -0
  108. package/dist/server/renderer/extract.js +149 -31
  109. package/dist/server/renderer/handler.jsx +450 -0
  110. package/dist/server/renderer/index.js +8 -1
  111. package/dist/server/renderer/render.js +47 -0
  112. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  113. package/dist/server/utils/cjsRequire.js +10 -0
  114. package/dist/server/utils/userAgentUtil.js +87 -9
  115. package/dist/server/utils/validator.js +164 -1
  116. package/dist/vite/FastRefresh.jsx +20 -0
  117. package/dist/vite/inject-cache-key-plugin.js +276 -0
  118. package/dist/vite/loadCustomViteConfig.js +22 -0
  119. package/dist/vite/manifest-categorization-plugin.js +455 -0
  120. package/dist/vite/node-loader.mjs +458 -0
  121. package/dist/vite/scssParams.js +21 -0
  122. package/dist/vite/vite.config.client.js +130 -0
  123. package/dist/vite/vite.config.js +498 -0
  124. package/dist/vite/vite.config.server.js +84 -0
  125. package/dist/web-router/components/MetaTag.jsx +34 -0
  126. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  127. package/dist/web-router/components/Split.jsx +200 -0
  128. package/dist/web-router/components/SplitInview.jsx +106 -0
  129. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  130. package/dist/web-router/context.jsx +12 -0
  131. package/dist/web-router/hooks.jsx +72 -0
  132. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  133. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  134. package/dist/web-router/types/context.d.ts +14 -0
  135. package/dist/web-router/types/hooks.d.ts +1 -0
  136. package/dist/web-router/types/index.d.ts +3 -0
  137. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  138. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  139. package/mcp_v2/conversion-tasks.json +35 -17
  140. package/mcp_v2/knowledge-base.json +176 -17
  141. package/mcp_v2/lib/helpers.js +10 -0
  142. package/mcp_v2/mcp.js +40 -8
  143. package/mcp_v2/package.json +1 -0
  144. package/mcp_v2/tools/build.js +161 -37
  145. package/mcp_v2/tools/conversion.js +106 -5
  146. package/mcp_v2/tools/tasks.js +18 -14
  147. package/package.json +35 -68
  148. package/babel.config.js +0 -32
  149. package/dist/index.js +0 -1
  150. package/dist/scripts/devBuild.js +0 -3
  151. package/dist/scripts/devServe.js +0 -3
  152. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  153. package/dist/scripts/registerAliases.js +0 -6
  154. package/dist/server/renderer/document/Body.js +0 -11
  155. package/dist/server/renderer/document/Head.js +0 -9
  156. package/dist/server/renderer/document/index.js +0 -1
  157. package/dist/server/renderer/handler.js +0 -47
  158. package/dist/server/startServer.js +0 -57
  159. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  160. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  161. package/dist/webpack/babel.config.client.js +0 -1
  162. package/dist/webpack/babel.config.ssr.js +0 -1
  163. package/dist/webpack/base.babel.js +0 -7
  164. package/dist/webpack/development.client.babel.js +0 -10
  165. package/dist/webpack/production.client.babel.js +0 -4
  166. package/dist/webpack/production.ssr.babel.js +0 -8
  167. package/dist/webpack/scssParams.js +0 -9
@@ -1 +1,174 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildSummary=buildSummary;const SLOW_MS={interaction:300,request:500,nativeApi:150,render:50};function round(value){return Number.isFinite(value)?Math.round(value):value;}function durationOf(record){if(Number.isFinite(record?.durationMs))return record.durationMs;if(Number.isFinite(record?.startTime)&&Number.isFinite(record?.endTime)){return Math.max(0,record.endTime-record.startTime);}return 0;}function byDurationDesc(a,b){return durationOf(b)-durationOf(a);}function overlaps(record,start,end,padding=0){if(!Number.isFinite(start)||!Number.isFinite(end))return false;const recordStart=record.startTime??0;const recordEnd=record.endTime??recordStart;return recordStart<end+padding&&recordEnd>=start-padding;}function top(records,count=5){return[...records].sort(byDurationDesc).slice(0,count);}function countBy(records,key){return records.reduce((acc,record)=>{const value=record?.[key]??"unknown";acc[value]=(acc[value]??0)+1;return acc;},{});}function summarizeInteraction(interaction,data){const start=interaction.startTime;const end=interaction.endTime;const sessionId=interaction.sessionId??interaction.detail?.sessionId;const related=(record,padding=150)=>{const detail=record.detail??{};return detail.sessionId===sessionId||detail.interactionId===sessionId||record.sessionId===sessionId||record.interactionId===sessionId||overlaps(record,start,end,padding);};const requests=data.requests.filter(request=>related(request));const render=data.metrics.filter(metric=>["fps-drop","long-task","loaf","layout-shift"].includes(metric.kind)).filter(metric=>related(metric,300));const insights=data.insights.filter(insight=>related(insight,300));const nativeApiTime=requests.filter(request=>request.kind==="native-api").reduce((total,request)=>total+durationOf(request),0);const networkTime=requests.filter(request=>request.kind==="network").reduce((total,request)=>total+durationOf(request),0);const slowestRequest=top(requests,1)[0]??null;return{id:interaction.id,label:interaction.label,target:interaction.target??interaction.detail?.target??null,startTime:round(start),durationMs:round(durationOf(interaction)),requestCount:requests.length,renderIssueCount:render.length,insightCount:insights.length,nativeApiTimeMs:round(nativeApiTime),networkTimeMs:round(networkTime),slowestRequest:slowestRequest?{id:slowestRequest.id,kind:slowestRequest.kind,label:slowestRequest.label,durationMs:round(durationOf(slowestRequest))}:null,severity:durationOf(interaction)>=SLOW_MS.interaction||render.length>0||insights.length>0?"warning":"ok"};}function buildFindings(data,interactionSummaries){const findings=[];const slowInteractions=interactionSummaries.filter(interaction=>interaction.durationMs>=SLOW_MS.interaction);const slowRequests=data.requests.filter(request=>durationOf(request)>=SLOW_MS.request);const slowNativeApi=data.requests.filter(request=>request.kind==="native-api"&&durationOf(request)>=SLOW_MS.nativeApi);const renderIssues=data.metrics.filter(metric=>["fps-drop","long-task","loaf","layout-shift"].includes(metric.kind));const cacheMisses=data.requests.filter(request=>request.cacheStatus==="miss");if(slowInteractions.length>0){findings.push({severity:"warning",title:`${slowInteractions.length} slow interaction${slowInteractions.length===1?"":"s"}`,detail:`${slowInteractions[0].label} took ${slowInteractions[0].durationMs}ms`});}if(slowRequests.length>0){const slowest=top(slowRequests,1)[0];findings.push({severity:"warning",title:`${slowRequests.length} slow request${slowRequests.length===1?"":"s"}`,detail:`${slowest.label} took ${round(durationOf(slowest))}ms`});}if(slowNativeApi.length>0){const slowest=top(slowNativeApi,1)[0];findings.push({severity:"warning",title:`${slowNativeApi.length} slow native call${slowNativeApi.length===1?"":"s"}`,detail:`${slowest.label} took ${round(durationOf(slowest))}ms`});}if(renderIssues.length>0){findings.push({severity:"warning",title:`${renderIssues.length} render issue${renderIssues.length===1?"":"s"}`,detail:renderIssues[0].label});}if(cacheMisses.length>0){findings.push({severity:"info",title:`${cacheMisses.length} cache miss${cacheMisses.length===1?"":"es"}`,detail:cacheMisses[0].label});}return findings;}function buildSummary(data){const sessions=data.sessions??[];const requests=data.requests??[];const metrics=data.metrics??[];const events=data.events??[];const insights=data.insights??[];const interactions=sessions.filter(session=>session.kind==="interaction");const renderIssues=metrics.filter(metric=>["fps-drop","long-task","loaf","layout-shift"].includes(metric.kind));const memory=metrics.find(metric=>metric.kind==="memory-summary")??null;const interactionSummaries=top(interactions,10).map(interaction=>summarizeInteraction(interaction,data));return{generatedAt:new Date().toISOString(),counts:{events:events.length,sessions:sessions.length,requests:requests.length,metrics:metrics.length,insights:insights.length},byKind:{sessions:countBy(sessions,"kind"),requests:countBy(requests,"kind"),metrics:countBy(metrics,"kind")},slowest:{interactions:top(interactions).map(record=>({id:record.id,label:record.label,durationMs:round(durationOf(record))})),requests:top(requests).map(record=>({id:record.id,kind:record.kind,label:record.label,durationMs:round(durationOf(record))})),render:top(renderIssues).map(record=>({id:record.id,kind:record.kind,label:record.label,durationMs:round(durationOf(record))}))},interactions:interactionSummaries,memory:memory?{label:memory.label,latest:memory.detail?.latest??{},peak:memory.detail?.peak??{},sampleCount:memory.detail?.sampleCount??0}:null,findings:buildFindings(data,interactionSummaries)};}
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildSummary = buildSummary;
7
+ const SLOW_MS = {
8
+ interaction: 300,
9
+ request: 500,
10
+ nativeApi: 150,
11
+ render: 50
12
+ };
13
+ function round(value) {
14
+ return Number.isFinite(value) ? Math.round(value) : value;
15
+ }
16
+ function durationOf(record) {
17
+ if (Number.isFinite(record?.durationMs)) return record.durationMs;
18
+ if (Number.isFinite(record?.startTime) && Number.isFinite(record?.endTime)) {
19
+ return Math.max(0, record.endTime - record.startTime);
20
+ }
21
+ return 0;
22
+ }
23
+ function byDurationDesc(a, b) {
24
+ return durationOf(b) - durationOf(a);
25
+ }
26
+ function overlaps(record, start, end, padding = 0) {
27
+ if (!Number.isFinite(start) || !Number.isFinite(end)) return false;
28
+ const recordStart = record.startTime ?? 0;
29
+ const recordEnd = record.endTime ?? recordStart;
30
+ return recordStart < end + padding && recordEnd >= start - padding;
31
+ }
32
+ function top(records, count = 5) {
33
+ return [...records].sort(byDurationDesc).slice(0, count);
34
+ }
35
+ function countBy(records, key) {
36
+ return records.reduce((acc, record) => {
37
+ const value = record?.[key] ?? "unknown";
38
+ acc[value] = (acc[value] ?? 0) + 1;
39
+ return acc;
40
+ }, {});
41
+ }
42
+ function summarizeInteraction(interaction, data) {
43
+ const start = interaction.startTime;
44
+ const end = interaction.endTime;
45
+ const sessionId = interaction.sessionId ?? interaction.detail?.sessionId;
46
+ const related = (record, padding = 150) => {
47
+ const detail = record.detail ?? {};
48
+ return detail.sessionId === sessionId || detail.interactionId === sessionId || record.sessionId === sessionId || record.interactionId === sessionId || overlaps(record, start, end, padding);
49
+ };
50
+ const requests = data.requests.filter(request => related(request));
51
+ const render = data.metrics.filter(metric => ["fps-drop", "long-task", "loaf", "layout-shift"].includes(metric.kind)).filter(metric => related(metric, 300));
52
+ const insights = data.insights.filter(insight => related(insight, 300));
53
+ const nativeApiTime = requests.filter(request => request.kind === "native-api").reduce((total, request) => total + durationOf(request), 0);
54
+ const networkTime = requests.filter(request => request.kind === "network").reduce((total, request) => total + durationOf(request), 0);
55
+ const slowestRequest = top(requests, 1)[0] ?? null;
56
+ return {
57
+ id: interaction.id,
58
+ label: interaction.label,
59
+ target: interaction.target ?? interaction.detail?.target ?? null,
60
+ startTime: round(start),
61
+ durationMs: round(durationOf(interaction)),
62
+ requestCount: requests.length,
63
+ renderIssueCount: render.length,
64
+ insightCount: insights.length,
65
+ nativeApiTimeMs: round(nativeApiTime),
66
+ networkTimeMs: round(networkTime),
67
+ slowestRequest: slowestRequest ? {
68
+ id: slowestRequest.id,
69
+ kind: slowestRequest.kind,
70
+ label: slowestRequest.label,
71
+ durationMs: round(durationOf(slowestRequest))
72
+ } : null,
73
+ severity: durationOf(interaction) >= SLOW_MS.interaction || render.length > 0 || insights.length > 0 ? "warning" : "ok"
74
+ };
75
+ }
76
+ function buildFindings(data, interactionSummaries) {
77
+ const findings = [];
78
+ const slowInteractions = interactionSummaries.filter(interaction => interaction.durationMs >= SLOW_MS.interaction);
79
+ const slowRequests = data.requests.filter(request => durationOf(request) >= SLOW_MS.request);
80
+ const slowNativeApi = data.requests.filter(request => request.kind === "native-api" && durationOf(request) >= SLOW_MS.nativeApi);
81
+ const renderIssues = data.metrics.filter(metric => ["fps-drop", "long-task", "loaf", "layout-shift"].includes(metric.kind));
82
+ const cacheMisses = data.requests.filter(request => request.cacheStatus === "miss");
83
+ if (slowInteractions.length > 0) {
84
+ findings.push({
85
+ severity: "warning",
86
+ title: `${slowInteractions.length} slow interaction${slowInteractions.length === 1 ? "" : "s"}`,
87
+ detail: `${slowInteractions[0].label} took ${slowInteractions[0].durationMs}ms`
88
+ });
89
+ }
90
+ if (slowRequests.length > 0) {
91
+ const slowest = top(slowRequests, 1)[0];
92
+ findings.push({
93
+ severity: "warning",
94
+ title: `${slowRequests.length} slow request${slowRequests.length === 1 ? "" : "s"}`,
95
+ detail: `${slowest.label} took ${round(durationOf(slowest))}ms`
96
+ });
97
+ }
98
+ if (slowNativeApi.length > 0) {
99
+ const slowest = top(slowNativeApi, 1)[0];
100
+ findings.push({
101
+ severity: "warning",
102
+ title: `${slowNativeApi.length} slow native call${slowNativeApi.length === 1 ? "" : "s"}`,
103
+ detail: `${slowest.label} took ${round(durationOf(slowest))}ms`
104
+ });
105
+ }
106
+ if (renderIssues.length > 0) {
107
+ findings.push({
108
+ severity: "warning",
109
+ title: `${renderIssues.length} render issue${renderIssues.length === 1 ? "" : "s"}`,
110
+ detail: renderIssues[0].label
111
+ });
112
+ }
113
+ if (cacheMisses.length > 0) {
114
+ findings.push({
115
+ severity: "info",
116
+ title: `${cacheMisses.length} cache miss${cacheMisses.length === 1 ? "" : "es"}`,
117
+ detail: cacheMisses[0].label
118
+ });
119
+ }
120
+ return findings;
121
+ }
122
+ function buildSummary(data) {
123
+ const sessions = data.sessions ?? [];
124
+ const requests = data.requests ?? [];
125
+ const metrics = data.metrics ?? [];
126
+ const events = data.events ?? [];
127
+ const insights = data.insights ?? [];
128
+ const interactions = sessions.filter(session => session.kind === "interaction");
129
+ const renderIssues = metrics.filter(metric => ["fps-drop", "long-task", "loaf", "layout-shift"].includes(metric.kind));
130
+ const memory = metrics.find(metric => metric.kind === "memory-summary") ?? null;
131
+ const interactionSummaries = top(interactions, 10).map(interaction => summarizeInteraction(interaction, data));
132
+ return {
133
+ generatedAt: new Date().toISOString(),
134
+ counts: {
135
+ events: events.length,
136
+ sessions: sessions.length,
137
+ requests: requests.length,
138
+ metrics: metrics.length,
139
+ insights: insights.length
140
+ },
141
+ byKind: {
142
+ sessions: countBy(sessions, "kind"),
143
+ requests: countBy(requests, "kind"),
144
+ metrics: countBy(metrics, "kind")
145
+ },
146
+ slowest: {
147
+ interactions: top(interactions).map(record => ({
148
+ id: record.id,
149
+ label: record.label,
150
+ durationMs: round(durationOf(record))
151
+ })),
152
+ requests: top(requests).map(record => ({
153
+ id: record.id,
154
+ kind: record.kind,
155
+ label: record.label,
156
+ durationMs: round(durationOf(record))
157
+ })),
158
+ render: top(renderIssues).map(record => ({
159
+ id: record.id,
160
+ kind: record.kind,
161
+ label: record.label,
162
+ durationMs: round(durationOf(record))
163
+ }))
164
+ },
165
+ interactions: interactionSummaries,
166
+ memory: memory ? {
167
+ label: memory.label,
168
+ latest: memory.detail?.latest ?? {},
169
+ peak: memory.detail?.peak ?? {},
170
+ sampleCount: memory.detail?.sampleCount ?? 0
171
+ } : null,
172
+ findings: buildFindings(data, interactionSummaries)
173
+ };
174
+ }
@@ -1 +1,240 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildChromeTrace=buildChromeTrace;exports.downloadChromeTrace=downloadChromeTrace;const THREADS={navigation:1,input:2,render:3,memory:4,network:5,nativeApi:6,insights:7,events:8};const THREAD_NAMES={[THREADS.navigation]:"Navigation",[THREADS.input]:"Input",[THREADS.render]:"Render",[THREADS.memory]:"Memory",[THREADS.network]:"Network / Cache",[THREADS.nativeApi]:"Native API execution",[THREADS.insights]:"Insights",[THREADS.events]:"Events"};function us(ms){return Math.max(0,Math.round((Number(ms)||0)*1000));}function durationMs(record){if(Number.isFinite(record.durationMs))return Math.max(0.001,record.durationMs);if(Number.isFinite(record.startTime)&&Number.isFinite(record.endTime)){return Math.max(0.001,record.endTime-record.startTime);}return 0.001;}function label(record,fallback){return record.label??record.title??record.name??record.type??record.kind??fallback;}function detail(record){const source=record.detail??record;const relatedTap=record.interactionId??record.sessionId??source.interactionId??source.sessionId;return relatedTap?{relatedTap,...source}:source;}function displayLabel(record,fallback){const base=label(record,fallback);const source=record.detail??record;const relatedTap=record.interactionId??record.sessionId??source.interactionId??source.sessionId;if(!relatedTap)return base;if(record.kind==="interaction")return`${relatedTap}: ${record.target??source.target??base}`;return base.startsWith(`[${relatedTap}]`)?base:`[${relatedTap}] ${base}`;}function sessionThread(kind){if(kind==="page-load"||kind==="route")return THREADS.navigation;if(kind==="scroll"||kind==="keyboard"||kind==="interaction")return THREADS.input;if(kind==="hw-accel"||kind==="hook")return THREADS.render;return THREADS.events;}function requestThread(kind){if(kind==="native-api")return THREADS.nativeApi;return THREADS.network;}function metricThread(kind){if(kind==="memory"||kind==="memory-summary")return THREADS.memory;if(kind==="startup"||kind==="paint")return THREADS.navigation;return THREADS.render;}function addMetadata(events,pid){events.push({ph:"M",pid,tid:0,name:"process_name",args:{name:"Catalyst"}});for(const[tid,name]of Object.entries(THREAD_NAMES)){events.push({ph:"M",pid,tid:Number(tid),name:"thread_name",args:{name}});}}function addDuration(events,record,tid,category,fallbackName){const startTime=record.startTime??record.time??0;events.push({name:displayLabel(record,fallbackName),cat:category,ph:"X",ts:us(startTime),dur:us(durationMs(record)),pid:1,tid,args:detail(record)});}function addInstant(events,record,tid,category,fallbackName){events.push({name:displayLabel(record,fallbackName),cat:category,ph:"i",s:"t",ts:us(record.startTime??record.time??0),pid:1,tid,args:detail(record)});}function addTapMarker(events,session){const source=session.detail??session;const tap=session.interactionId??session.sessionId??source.interactionId??source.sessionId;if(!tap)return;events.push({name:`${tap}: ${session.target??source.target??"interaction"}`,cat:"Catalyst Interaction",ph:"i",s:"g",ts:us(session.startTime??0),pid:1,tid:THREADS.input,args:{relatedTap:tap,target:session.target??source.target??"unknown"}});}function addCounter(events,record){const args={};const source=record.detail??record;for(const key of["jvmMb","webviewMb","nativeMb","otherMb","totalMb","peakMb"]){if(Number.isFinite(source[key]))args[key]=source[key];}if(Object.keys(args).length===0&&Number.isFinite(record.value)){args.value=record.value;}events.push({name:label(record,"Memory"),cat:"Catalyst Memory",ph:"C",ts:us(record.startTime??record.time??0),pid:1,tid:THREADS.memory,args});}function buildChromeTrace(data){const traceEvents=[];addMetadata(traceEvents,1);for(const session of data.sessions??[]){addDuration(traceEvents,session,sessionThread(session.kind),`Catalyst ${session.kind}`,"Session");if(session.kind==="interaction")addTapMarker(traceEvents,session);}for(const request of data.requests??[]){addDuration(traceEvents,request,requestThread(request.kind),`Catalyst ${request.kind}`,"Request");}const hasMemorySamples=(data.metrics??[]).some(metric=>metric.kind==="memory");for(const metric of data.metrics??[]){if(metric.kind==="memory-summary"&&hasMemorySamples)continue;if(metric.kind==="memory"||metric.kind==="memory-summary"){addCounter(traceEvents,metric);}else{addDuration(traceEvents,metric,metricThread(metric.kind),`Catalyst ${metric.kind}`,"Metric");}}for(const event of data.events??[]){addInstant(traceEvents,event,THREADS.events,`Catalyst ${event.kind??"event"}`,"Event");}for(const insight of data.insights??[]){addInstant(traceEvents,insight,THREADS.insights,"Catalyst Insights","Insight");}return{traceEvents:traceEvents.sort((a,b)=>(a.ts??0)-(b.ts??0)),metadata:{source:"CatalystPerf",createdAt:new Date().toISOString(),storeCreatedAt:data.createdAt??null,version:data.version??1}};}function downloadChromeTrace(data,filename){const trace=buildChromeTrace(data);const traceJson=JSON.stringify(trace);const resolvedFilename=filename??`catalyst-trace-${new Date().toISOString().replace(/[:.]/g,"-")}.json`;const eventCount=trace.traceEvents.length;const isIOSNative=typeof window!=="undefined"&&!!window.webkit?.messageHandlers?.NativeBridge;const isAndroidNative=typeof window!=="undefined"&&!!window.NativeBridge;if(typeof console!=="undefined"){console.info(`[CatalystPerf] Trace ready: ${resolvedFilename} (${eventCount} events)`);}if(isIOSNative){window.webkit.messageHandlers.NativeBridge.postMessage({command:"exportCatalystTrace",data:{filename:resolvedFilename,trace:traceJson}});if(typeof console!=="undefined"){console.info(`[CatalystPerf] Sent ${resolvedFilename} to the iOS share sheet.`);}return;}if(isAndroidNative){return;}if(typeof document==="undefined"||typeof Blob==="undefined"||typeof URL==="undefined"){return;}const link=document.createElement("a");if(!("download"in link)){if(typeof console!=="undefined"){console.warn("[CatalystPerf] Browser does not support blob downloads. Use CatalystPerf.trace() instead.");}return;}const blob=new Blob([traceJson],{type:"application/json"});const url=URL.createObjectURL(blob);link.href=url;link.download=resolvedFilename;document.body.appendChild(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000);if(typeof console!=="undefined"){console.info(`[CatalystPerf] Download started. Check your browser Downloads for ${resolvedFilename}.`);}}
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildChromeTrace = buildChromeTrace;
7
+ exports.downloadChromeTrace = downloadChromeTrace;
8
+ const THREADS = {
9
+ navigation: 1,
10
+ input: 2,
11
+ render: 3,
12
+ memory: 4,
13
+ network: 5,
14
+ nativeApi: 6,
15
+ insights: 7,
16
+ events: 8
17
+ };
18
+ const THREAD_NAMES = {
19
+ [THREADS.navigation]: "Navigation",
20
+ [THREADS.input]: "Input",
21
+ [THREADS.render]: "Render",
22
+ [THREADS.memory]: "Memory",
23
+ [THREADS.network]: "Network / Cache",
24
+ [THREADS.nativeApi]: "Native API execution",
25
+ [THREADS.insights]: "Insights",
26
+ [THREADS.events]: "Events"
27
+ };
28
+ function us(ms) {
29
+ return Math.max(0, Math.round((Number(ms) || 0) * 1000));
30
+ }
31
+ function durationMs(record) {
32
+ if (Number.isFinite(record.durationMs)) return Math.max(0.001, record.durationMs);
33
+ if (Number.isFinite(record.startTime) && Number.isFinite(record.endTime)) {
34
+ return Math.max(0.001, record.endTime - record.startTime);
35
+ }
36
+ return 0.001;
37
+ }
38
+ function label(record, fallback) {
39
+ return record.label ?? record.title ?? record.name ?? record.type ?? record.kind ?? fallback;
40
+ }
41
+ function detail(record) {
42
+ const source = record.detail ?? record;
43
+ const relatedTap = record.interactionId ?? record.sessionId ?? source.interactionId ?? source.sessionId;
44
+ return relatedTap ? {
45
+ relatedTap,
46
+ ...source
47
+ } : source;
48
+ }
49
+ function displayLabel(record, fallback) {
50
+ const base = label(record, fallback);
51
+ const source = record.detail ?? record;
52
+ const relatedTap = record.interactionId ?? record.sessionId ?? source.interactionId ?? source.sessionId;
53
+ if (!relatedTap) return base;
54
+ if (record.kind === "interaction") return `${relatedTap}: ${record.target ?? source.target ?? base}`;
55
+ return base.startsWith(`[${relatedTap}]`) ? base : `[${relatedTap}] ${base}`;
56
+ }
57
+ function sessionThread(kind) {
58
+ if (kind === "page-load" || kind === "route") return THREADS.navigation;
59
+ if (kind === "scroll" || kind === "keyboard" || kind === "interaction") return THREADS.input;
60
+ if (kind === "hw-accel" || kind === "hook") return THREADS.render;
61
+ return THREADS.events;
62
+ }
63
+ function requestThread(kind) {
64
+ if (kind === "native-api") return THREADS.nativeApi;
65
+ return THREADS.network;
66
+ }
67
+ function metricThread(kind) {
68
+ if (kind === "memory" || kind === "memory-summary") return THREADS.memory;
69
+ if (kind === "startup" || kind === "paint") return THREADS.navigation;
70
+ return THREADS.render;
71
+ }
72
+ function addMetadata(events, pid) {
73
+ events.push({
74
+ ph: "M",
75
+ pid,
76
+ tid: 0,
77
+ name: "process_name",
78
+ args: {
79
+ name: "Catalyst"
80
+ }
81
+ });
82
+ for (const [tid, name] of Object.entries(THREAD_NAMES)) {
83
+ events.push({
84
+ ph: "M",
85
+ pid,
86
+ tid: Number(tid),
87
+ name: "thread_name",
88
+ args: {
89
+ name
90
+ }
91
+ });
92
+ }
93
+ }
94
+ function addDuration(events, record, tid, category, fallbackName) {
95
+ const startTime = record.startTime ?? record.time ?? 0;
96
+ events.push({
97
+ name: displayLabel(record, fallbackName),
98
+ cat: category,
99
+ ph: "X",
100
+ ts: us(startTime),
101
+ dur: us(durationMs(record)),
102
+ pid: 1,
103
+ tid,
104
+ args: detail(record)
105
+ });
106
+ }
107
+ function addInstant(events, record, tid, category, fallbackName) {
108
+ events.push({
109
+ name: displayLabel(record, fallbackName),
110
+ cat: category,
111
+ ph: "i",
112
+ s: "t",
113
+ ts: us(record.startTime ?? record.time ?? 0),
114
+ pid: 1,
115
+ tid,
116
+ args: detail(record)
117
+ });
118
+ }
119
+ function addTapMarker(events, session) {
120
+ const source = session.detail ?? session;
121
+ const tap = session.interactionId ?? session.sessionId ?? source.interactionId ?? source.sessionId;
122
+ if (!tap) return;
123
+ events.push({
124
+ name: `${tap}: ${session.target ?? source.target ?? "interaction"}`,
125
+ cat: "Catalyst Interaction",
126
+ ph: "i",
127
+ s: "g",
128
+ ts: us(session.startTime ?? 0),
129
+ pid: 1,
130
+ tid: THREADS.input,
131
+ args: {
132
+ relatedTap: tap,
133
+ target: session.target ?? source.target ?? "unknown"
134
+ }
135
+ });
136
+ }
137
+ function addCounter(events, record) {
138
+ const args = {};
139
+ const source = record.detail ?? record;
140
+ for (const key of ["jvmMb", "webviewMb", "nativeMb", "otherMb", "totalMb", "peakMb"]) {
141
+ if (Number.isFinite(source[key])) args[key] = source[key];
142
+ }
143
+ if (Object.keys(args).length === 0 && Number.isFinite(record.value)) {
144
+ args.value = record.value;
145
+ }
146
+ events.push({
147
+ name: label(record, "Memory"),
148
+ cat: "Catalyst Memory",
149
+ ph: "C",
150
+ ts: us(record.startTime ?? record.time ?? 0),
151
+ pid: 1,
152
+ tid: THREADS.memory,
153
+ args
154
+ });
155
+ }
156
+ function buildChromeTrace(data) {
157
+ const traceEvents = [];
158
+ addMetadata(traceEvents, 1);
159
+ for (const session of data.sessions ?? []) {
160
+ addDuration(traceEvents, session, sessionThread(session.kind), `Catalyst ${session.kind}`, "Session");
161
+ if (session.kind === "interaction") addTapMarker(traceEvents, session);
162
+ }
163
+ for (const request of data.requests ?? []) {
164
+ addDuration(traceEvents, request, requestThread(request.kind), `Catalyst ${request.kind}`, "Request");
165
+ }
166
+ const hasMemorySamples = (data.metrics ?? []).some(metric => metric.kind === "memory");
167
+ for (const metric of data.metrics ?? []) {
168
+ if (metric.kind === "memory-summary" && hasMemorySamples) continue;
169
+ if (metric.kind === "memory" || metric.kind === "memory-summary") {
170
+ addCounter(traceEvents, metric);
171
+ } else {
172
+ addDuration(traceEvents, metric, metricThread(metric.kind), `Catalyst ${metric.kind}`, "Metric");
173
+ }
174
+ }
175
+ for (const event of data.events ?? []) {
176
+ addInstant(traceEvents, event, THREADS.events, `Catalyst ${event.kind ?? "event"}`, "Event");
177
+ }
178
+ for (const insight of data.insights ?? []) {
179
+ addInstant(traceEvents, insight, THREADS.insights, "Catalyst Insights", "Insight");
180
+ }
181
+ return {
182
+ traceEvents: traceEvents.sort((a, b) => (a.ts ?? 0) - (b.ts ?? 0)),
183
+ metadata: {
184
+ source: "CatalystPerf",
185
+ createdAt: new Date().toISOString(),
186
+ storeCreatedAt: data.createdAt ?? null,
187
+ version: data.version ?? 1
188
+ }
189
+ };
190
+ }
191
+ function downloadChromeTrace(data, filename) {
192
+ const trace = buildChromeTrace(data);
193
+ const traceJson = JSON.stringify(trace);
194
+ const resolvedFilename = filename ?? `catalyst-trace-${new Date().toISOString().replace(/[:.]/g, "-")}.json`;
195
+ const eventCount = trace.traceEvents.length;
196
+ const isIOSNative = typeof window !== "undefined" && !!window.webkit?.messageHandlers?.NativeBridge;
197
+ const isAndroidNative = typeof window !== "undefined" && !!window.NativeBridge;
198
+ if (typeof console !== "undefined") {
199
+ console.info(`[CatalystPerf] Trace ready: ${resolvedFilename} (${eventCount} events)`);
200
+ }
201
+ if (isIOSNative) {
202
+ window.webkit.messageHandlers.NativeBridge.postMessage({
203
+ command: "exportCatalystTrace",
204
+ data: {
205
+ filename: resolvedFilename,
206
+ trace: traceJson
207
+ }
208
+ });
209
+ if (typeof console !== "undefined") {
210
+ console.info(`[CatalystPerf] Sent ${resolvedFilename} to the iOS share sheet.`);
211
+ }
212
+ return;
213
+ }
214
+ if (isAndroidNative) {
215
+ return;
216
+ }
217
+ if (typeof document === "undefined" || typeof Blob === "undefined" || typeof URL === "undefined") {
218
+ return;
219
+ }
220
+ const link = document.createElement("a");
221
+ if (!("download" in link)) {
222
+ if (typeof console !== "undefined") {
223
+ console.warn("[CatalystPerf] Browser does not support blob downloads. Use CatalystPerf.trace() instead.");
224
+ }
225
+ return;
226
+ }
227
+ const blob = new Blob([traceJson], {
228
+ type: "application/json"
229
+ });
230
+ const url = URL.createObjectURL(blob);
231
+ link.href = url;
232
+ link.download = resolvedFilename;
233
+ document.body.appendChild(link);
234
+ link.click();
235
+ link.remove();
236
+ setTimeout(() => URL.revokeObjectURL(url), 30000);
237
+ if (typeof console !== "undefined") {
238
+ console.info(`[CatalystPerf] Download started. Check your browser Downloads for ${resolvedFilename}.`);
239
+ }
240
+ }
@@ -1 +1,146 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildWaterfall=buildWaterfall;exports.listWaterfalls=listWaterfalls;const REQUEST_LANES=new Set(["network","cache","native-api"]);function durationOf(row){if(Number.isFinite(row.durationMs))return row.durationMs;if(Number.isFinite(row.startTime)&&Number.isFinite(row.endTime)){return Math.max(0,Math.round(row.endTime-row.startTime));}return null;}function toRow(record,lane,group=null){const startTime=record.startTime??record.time??0;const endTime=record.endTime??startTime;return{id:record.id,lane,group,kind:record.kind??record.type??lane,label:record.label??record.name??record.type??record.kind??lane,startTime,endTime,durationMs:durationOf({...record,startTime,endTime}),severity:record.severity??null,detail:record.detail??record};}function byStartTime(a,b){return(a.startTime??0)-(b.startTime??0);}function overlaps(row,start,end){if(!Number.isFinite(start)||!Number.isFinite(end))return false;const rowStart=row.startTime??0;const rowEnd=row.endTime??rowStart;return rowStart<end&&rowEnd>=start;}function isStandaloneEvent(event){return event.kind==="startup"||event.type==="navigation-back"||event.type==="page-load-error";}function isTimelineMetric(metric){return metric.kind!=="memory"&&metric.kind!=="memory-summary";}function rowsForWindow(data,start,end,group){return[...data.sessions.map(session=>toRow(session,session.kind,group)),...data.requests.map(request=>toRow(request,request.kind,group)),...data.metrics.filter(isTimelineMetric).map(metric=>toRow(metric,metric.kind,group)),...data.events.filter(isStandaloneEvent).map(event=>toRow(event,event.kind??"event",group)),...data.insights.map(insight=>toRow(insight,"insight",group))].filter(row=>overlaps(row,start,end)).sort(byStartTime);}function buildPageLoadWaterfall(data){const pageLoads=data.sessions.filter(session=>session.kind==="page-load");if(pageLoads.length===0){return[...data.events.filter(event=>event.kind==="startup"||event.type?.startsWith("boot-")).map(event=>toRow(event,"startup","page-load")),...data.metrics.filter(metric=>metric.kind==="paint"||metric.kind==="startup").map(metric=>toRow(metric,metric.kind,"page-load"))].sort(byStartTime);}return pageLoads.flatMap((pageLoad,index)=>{const group=pageLoad.label;const startupRows=index===0?data.events.filter(event=>event.kind==="startup"&&(event.startTime??0)<=pageLoad.endTime).map(event=>toRow(event,"startup",group)):[];return[...startupRows,toRow(pageLoad,"page-load",group),...rowsForWindow(data,pageLoad.startTime,pageLoad.endTime,group).filter(row=>row.id!==pageLoad.id)];}).sort(byStartTime);}function buildRouteWaterfall(data){const routes=data.sessions.filter(session=>session.kind==="route");return routes.flatMap(route=>[toRow(route,"route",route.label),...rowsForWindow(data,route.startTime,route.endTime+1000,route.label).filter(row=>row.id!==route.id)]).sort(byStartTime);}function buildRequestWaterfall(data){return data.requests.filter(request=>REQUEST_LANES.has(request.kind)).map(request=>toRow(request,request.kind,request.sessionId??request.navigationId??null)).sort(byStartTime);}function buildNativeApiWaterfall(data){return data.requests.filter(request=>request.kind==="native-api").map(request=>toRow(request,request.kind,request.sessionId??null)).sort(byStartTime);}function buildInteractionWaterfall(data){const interactions=data.sessions.filter(session=>session.kind==="interaction");return interactions.flatMap(interaction=>{const group=interaction.label;const matchingSession=row=>{const detail=row.detail??{};return detail.sessionId===interaction.sessionId||detail.interactionId===interaction.sessionId||overlaps(row,interaction.startTime,interaction.endTime);};return[toRow(interaction,"interaction",group),...[...data.requests.map(request=>toRow(request,request.kind,group)),...data.metrics.filter(isTimelineMetric).map(metric=>toRow(metric,metric.kind,group)),...data.insights.map(insight=>toRow(insight,"insight",group))].filter(row=>row.id!==interaction.id&&matchingSession(row))];}).sort(byStartTime);}function buildRenderJankWaterfall(data){const renderKinds=new Set(["fps-drop","long-task","loaf","layout-shift","hw-accel"]);return[...data.metrics.filter(metric=>renderKinds.has(metric.kind)).map(metric=>toRow(metric,"render",metric.context??null)),...data.sessions.filter(session=>session.kind==="hw-accel").map(session=>toRow(session,"render",session.detail?.trigger??null))].sort(byStartTime);}function buildScrollWaterfall(data){const scrolls=data.sessions.filter(session=>session.kind==="scroll");return scrolls.flatMap(scroll=>[toRow(scroll,"scroll",scroll.label),...rowsForWindow(data,scroll.startTime,scroll.endTime,scroll.label).filter(row=>row.id!==scroll.id&&["fps-drop","long-task","loaf","layout-shift","insight"].includes(row.kind))]).sort(byStartTime);}function buildKeyboardWaterfall(data){const keyboards=data.sessions.filter(session=>session.kind==="keyboard");return keyboards.flatMap(keyboard=>[toRow(keyboard,"keyboard",keyboard.label),...rowsForWindow(data,keyboard.startTime,keyboard.endTime,keyboard.label).filter(row=>row.id!==keyboard.id&&["viewport-resize","scroll","layout-shift","insight"].includes(row.kind))]).sort(byStartTime);}function buildCacheWaterfall(data){return[...data.requests.filter(request=>request.kind==="cache").map(request=>toRow(request,"cache",request.cacheStatus??null)),...data.metrics.filter(metric=>metric.kind==="cache-summary").map(metric=>toRow(metric,"cache-summary"))].sort(byStartTime);}function buildMemoryTimeline(data){return data.metrics.filter(metric=>metric.kind==="memory"||metric.kind==="memory-summary").map(metric=>toRow(metric,"memory")).sort(byStartTime);}function buildAllWaterfall(data){return[...data.sessions.map(session=>toRow(session,session.kind)),...data.requests.map(request=>toRow(request,request.kind)),...data.metrics.filter(isTimelineMetric).map(metric=>toRow(metric,metric.kind)),...data.events.filter(isStandaloneEvent).map(event=>toRow(event,event.kind??"event")),...data.insights.map(insight=>toRow(insight,"insight"))].sort(byStartTime);}function buildWaterfall(data,type="all"){switch(type){case"page-load":case"page":return buildPageLoadWaterfall(data);case"route":case"navigation":return buildRouteWaterfall(data);case"requests":case"request":return buildRequestWaterfall(data);case"native-bridge":case"bridge":case"api":case"native-api":return buildNativeApiWaterfall(data);case"interactions":case"interaction":return buildInteractionWaterfall(data);case"render":case"jank":case"render-jank":return buildRenderJankWaterfall(data);case"scroll":return buildScrollWaterfall(data);case"keyboard":case"input":return buildKeyboardWaterfall(data);case"cache":return buildCacheWaterfall(data);case"memory":return buildMemoryTimeline(data);case"all":default:return buildAllWaterfall(data);}}function listWaterfalls(){return["page-load","navigation","requests","native-api","interactions","render-jank","scroll","keyboard","cache","memory","all"];}
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildWaterfall = buildWaterfall;
7
+ exports.listWaterfalls = listWaterfalls;
8
+ const REQUEST_LANES = new Set(["network", "cache", "native-api"]);
9
+ function durationOf(row) {
10
+ if (Number.isFinite(row.durationMs)) return row.durationMs;
11
+ if (Number.isFinite(row.startTime) && Number.isFinite(row.endTime)) {
12
+ return Math.max(0, Math.round(row.endTime - row.startTime));
13
+ }
14
+ return null;
15
+ }
16
+ function toRow(record, lane, group = null) {
17
+ const startTime = record.startTime ?? record.time ?? 0;
18
+ const endTime = record.endTime ?? startTime;
19
+ return {
20
+ id: record.id,
21
+ lane,
22
+ group,
23
+ kind: record.kind ?? record.type ?? lane,
24
+ label: record.label ?? record.name ?? record.type ?? record.kind ?? lane,
25
+ startTime,
26
+ endTime,
27
+ durationMs: durationOf({
28
+ ...record,
29
+ startTime,
30
+ endTime
31
+ }),
32
+ severity: record.severity ?? null,
33
+ detail: record.detail ?? record
34
+ };
35
+ }
36
+ function byStartTime(a, b) {
37
+ return (a.startTime ?? 0) - (b.startTime ?? 0);
38
+ }
39
+ function overlaps(row, start, end) {
40
+ if (!Number.isFinite(start) || !Number.isFinite(end)) return false;
41
+ const rowStart = row.startTime ?? 0;
42
+ const rowEnd = row.endTime ?? rowStart;
43
+ return rowStart < end && rowEnd >= start;
44
+ }
45
+ function isStandaloneEvent(event) {
46
+ return event.kind === "startup" || event.type === "navigation-back" || event.type === "page-load-error";
47
+ }
48
+ function isTimelineMetric(metric) {
49
+ return metric.kind !== "memory" && metric.kind !== "memory-summary";
50
+ }
51
+ function rowsForWindow(data, start, end, group) {
52
+ return [...data.sessions.map(session => toRow(session, session.kind, group)), ...data.requests.map(request => toRow(request, request.kind, group)), ...data.metrics.filter(isTimelineMetric).map(metric => toRow(metric, metric.kind, group)), ...data.events.filter(isStandaloneEvent).map(event => toRow(event, event.kind ?? "event", group)), ...data.insights.map(insight => toRow(insight, "insight", group))].filter(row => overlaps(row, start, end)).sort(byStartTime);
53
+ }
54
+ function buildPageLoadWaterfall(data) {
55
+ const pageLoads = data.sessions.filter(session => session.kind === "page-load");
56
+ if (pageLoads.length === 0) {
57
+ return [...data.events.filter(event => event.kind === "startup" || event.type?.startsWith("boot-")).map(event => toRow(event, "startup", "page-load")), ...data.metrics.filter(metric => metric.kind === "paint" || metric.kind === "startup").map(metric => toRow(metric, metric.kind, "page-load"))].sort(byStartTime);
58
+ }
59
+ return pageLoads.flatMap((pageLoad, index) => {
60
+ const group = pageLoad.label;
61
+ const startupRows = index === 0 ? data.events.filter(event => event.kind === "startup" && (event.startTime ?? 0) <= pageLoad.endTime).map(event => toRow(event, "startup", group)) : [];
62
+ return [...startupRows, toRow(pageLoad, "page-load", group), ...rowsForWindow(data, pageLoad.startTime, pageLoad.endTime, group).filter(row => row.id !== pageLoad.id)];
63
+ }).sort(byStartTime);
64
+ }
65
+ function buildRouteWaterfall(data) {
66
+ const routes = data.sessions.filter(session => session.kind === "route");
67
+ return routes.flatMap(route => [toRow(route, "route", route.label), ...rowsForWindow(data, route.startTime, route.endTime + 1000, route.label).filter(row => row.id !== route.id)]).sort(byStartTime);
68
+ }
69
+ function buildRequestWaterfall(data) {
70
+ return data.requests.filter(request => REQUEST_LANES.has(request.kind)).map(request => toRow(request, request.kind, request.sessionId ?? request.navigationId ?? null)).sort(byStartTime);
71
+ }
72
+ function buildNativeApiWaterfall(data) {
73
+ return data.requests.filter(request => request.kind === "native-api").map(request => toRow(request, request.kind, request.sessionId ?? null)).sort(byStartTime);
74
+ }
75
+ function buildInteractionWaterfall(data) {
76
+ const interactions = data.sessions.filter(session => session.kind === "interaction");
77
+ return interactions.flatMap(interaction => {
78
+ const group = interaction.label;
79
+ const matchingSession = row => {
80
+ const detail = row.detail ?? {};
81
+ return detail.sessionId === interaction.sessionId || detail.interactionId === interaction.sessionId || overlaps(row, interaction.startTime, interaction.endTime);
82
+ };
83
+ return [toRow(interaction, "interaction", group), ...[...data.requests.map(request => toRow(request, request.kind, group)), ...data.metrics.filter(isTimelineMetric).map(metric => toRow(metric, metric.kind, group)), ...data.insights.map(insight => toRow(insight, "insight", group))].filter(row => row.id !== interaction.id && matchingSession(row))];
84
+ }).sort(byStartTime);
85
+ }
86
+ function buildRenderJankWaterfall(data) {
87
+ const renderKinds = new Set(["fps-drop", "long-task", "loaf", "layout-shift", "hw-accel"]);
88
+ return [...data.metrics.filter(metric => renderKinds.has(metric.kind)).map(metric => toRow(metric, "render", metric.context ?? null)), ...data.sessions.filter(session => session.kind === "hw-accel").map(session => toRow(session, "render", session.detail?.trigger ?? null))].sort(byStartTime);
89
+ }
90
+ function buildScrollWaterfall(data) {
91
+ const scrolls = data.sessions.filter(session => session.kind === "scroll");
92
+ return scrolls.flatMap(scroll => [toRow(scroll, "scroll", scroll.label), ...rowsForWindow(data, scroll.startTime, scroll.endTime, scroll.label).filter(row => row.id !== scroll.id && ["fps-drop", "long-task", "loaf", "layout-shift", "insight"].includes(row.kind))]).sort(byStartTime);
93
+ }
94
+ function buildKeyboardWaterfall(data) {
95
+ const keyboards = data.sessions.filter(session => session.kind === "keyboard");
96
+ return keyboards.flatMap(keyboard => [toRow(keyboard, "keyboard", keyboard.label), ...rowsForWindow(data, keyboard.startTime, keyboard.endTime, keyboard.label).filter(row => row.id !== keyboard.id && ["viewport-resize", "scroll", "layout-shift", "insight"].includes(row.kind))]).sort(byStartTime);
97
+ }
98
+ function buildCacheWaterfall(data) {
99
+ return [...data.requests.filter(request => request.kind === "cache").map(request => toRow(request, "cache", request.cacheStatus ?? null)), ...data.metrics.filter(metric => metric.kind === "cache-summary").map(metric => toRow(metric, "cache-summary"))].sort(byStartTime);
100
+ }
101
+ function buildMemoryTimeline(data) {
102
+ return data.metrics.filter(metric => metric.kind === "memory" || metric.kind === "memory-summary").map(metric => toRow(metric, "memory")).sort(byStartTime);
103
+ }
104
+ function buildAllWaterfall(data) {
105
+ return [...data.sessions.map(session => toRow(session, session.kind)), ...data.requests.map(request => toRow(request, request.kind)), ...data.metrics.filter(isTimelineMetric).map(metric => toRow(metric, metric.kind)), ...data.events.filter(isStandaloneEvent).map(event => toRow(event, event.kind ?? "event")), ...data.insights.map(insight => toRow(insight, "insight"))].sort(byStartTime);
106
+ }
107
+ function buildWaterfall(data, type = "all") {
108
+ switch (type) {
109
+ case "page-load":
110
+ case "page":
111
+ return buildPageLoadWaterfall(data);
112
+ case "route":
113
+ case "navigation":
114
+ return buildRouteWaterfall(data);
115
+ case "requests":
116
+ case "request":
117
+ return buildRequestWaterfall(data);
118
+ case "native-bridge":
119
+ case "bridge":
120
+ case "api":
121
+ case "native-api":
122
+ return buildNativeApiWaterfall(data);
123
+ case "interactions":
124
+ case "interaction":
125
+ return buildInteractionWaterfall(data);
126
+ case "render":
127
+ case "jank":
128
+ case "render-jank":
129
+ return buildRenderJankWaterfall(data);
130
+ case "scroll":
131
+ return buildScrollWaterfall(data);
132
+ case "keyboard":
133
+ case "input":
134
+ return buildKeyboardWaterfall(data);
135
+ case "cache":
136
+ return buildCacheWaterfall(data);
137
+ case "memory":
138
+ return buildMemoryTimeline(data);
139
+ case "all":
140
+ default:
141
+ return buildAllWaterfall(data);
142
+ }
143
+ }
144
+ function listWaterfalls() {
145
+ return ["page-load", "navigation", "requests", "native-api", "interactions", "render-jank", "scroll", "keyboard", "cache", "memory", "all"];
146
+ }