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,289 @@
|
|
|
1
|
+
package io.yourname.androidproject.utils
|
|
2
|
+
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import android.os.SystemClock
|
|
6
|
+
import android.webkit.WebView
|
|
7
|
+
import io.yourname.androidproject.BuildConfig
|
|
8
|
+
import org.json.JSONArray
|
|
9
|
+
import org.json.JSONObject
|
|
10
|
+
import java.util.concurrent.ConcurrentLinkedQueue
|
|
11
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* PerfEventBuffer — native ring buffer for Catalyst profiler events.
|
|
15
|
+
*
|
|
16
|
+
* Collects all events from onCreate (boot timing, cache, API calls) and flushes
|
|
17
|
+
* them as a single JSON array to the web layer after onPageFinished.
|
|
18
|
+
*
|
|
19
|
+
* Design:
|
|
20
|
+
* - add() is safe from any thread (ConcurrentLinkedQueue)
|
|
21
|
+
* - scheduleFlush() called from onPageFinished — posts to main thread after 250ms
|
|
22
|
+
* so the JS environment is ready to receive events
|
|
23
|
+
* - flush() serialises all events to JSON array, calls window.__catalystPerfBatch()
|
|
24
|
+
* - CacheSummarizer: running tally of hit/miss/fetch + top-5 min-heap for InsightsCollector
|
|
25
|
+
* - Max ring buffer size: 500 events (oldest dropped if exceeded)
|
|
26
|
+
*
|
|
27
|
+
* API call timing:
|
|
28
|
+
* - bridgeCallReceived(callId, method) → stamps t0, stores in pendingCalls map
|
|
29
|
+
* - bridgeCallDispatched(callId) → stamps t1, computes durationMs, adds api-call event to buffer
|
|
30
|
+
*/
|
|
31
|
+
object PerfEventBuffer {
|
|
32
|
+
|
|
33
|
+
private const val TAG = "PerfEventBuffer"
|
|
34
|
+
private const val MAX_BUFFER_SIZE = 500
|
|
35
|
+
private const val FLUSH_DELAY_MS = 250L
|
|
36
|
+
private const val FLUSH_RETRY_DELAY_MS = 250L
|
|
37
|
+
private const val MAX_FLUSH_RETRIES = 5
|
|
38
|
+
private const val PERIODIC_FLUSH_INTERVAL_MS = 4000L
|
|
39
|
+
|
|
40
|
+
private val buffer = ConcurrentLinkedQueue<JSONObject>()
|
|
41
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
42
|
+
private val flushed = AtomicBoolean(false)
|
|
43
|
+
@Volatile private var profilerEnabled = false
|
|
44
|
+
private val deliveryState = PerfDeliveryState<JSONArray>()
|
|
45
|
+
|
|
46
|
+
// Periodic flush state
|
|
47
|
+
@Volatile private var periodicWebView: WebView? = null
|
|
48
|
+
private var lastFlushedSize = 0
|
|
49
|
+
private val periodicRunnable: Runnable = object : Runnable {
|
|
50
|
+
override fun run() {
|
|
51
|
+
if (!isEnabled()) return
|
|
52
|
+
val wv = periodicWebView ?: return
|
|
53
|
+
val pendingBatch = deliveryState.current()
|
|
54
|
+
if (pendingBatch != null) {
|
|
55
|
+
deliver(wv, pendingBatch)
|
|
56
|
+
} else if (buffer.size > lastFlushedSize) {
|
|
57
|
+
val currentSize = buffer.size
|
|
58
|
+
android.util.Log.d(TAG, "[PerfBuffer] periodicFlush() — $currentSize events since last flush")
|
|
59
|
+
beginFlush(wv, includeCacheSummary = false)
|
|
60
|
+
}
|
|
61
|
+
mainHandler.postDelayed(this, PERIODIC_FLUSH_INTERVAL_MS)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Pending API call timing: callId → t0 nativeMs + method
|
|
66
|
+
private val pendingCalls = java.util.concurrent.ConcurrentHashMap<String, Pair<Long, String>>()
|
|
67
|
+
|
|
68
|
+
// ─── CacheSummarizer ─────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
private var cacheHits = 0
|
|
71
|
+
private var cacheMisses = 0
|
|
72
|
+
private var cacheFetches = 0
|
|
73
|
+
private var cacheTotalMs = 0L
|
|
74
|
+
// top-5 slowest: list of Pair(durationMs, filename)
|
|
75
|
+
private val cacheTopSlow = java.util.PriorityQueue<Pair<Long, String>>(
|
|
76
|
+
6, compareBy { it.first }
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
private fun updateCacheSummary(type: String, durationMs: Long, url: String) {
|
|
80
|
+
val filename = try {
|
|
81
|
+
val path = java.net.URI(url).path
|
|
82
|
+
path.split("/").filter { it.isNotEmpty() }.lastOrNull()?.take(40) ?: url.take(40)
|
|
83
|
+
} catch (e: Exception) { url.take(40) }
|
|
84
|
+
|
|
85
|
+
when (type) {
|
|
86
|
+
"cache-hit-memory", "cache-hit-disk" -> cacheHits++
|
|
87
|
+
"cache-miss-fetch" -> cacheMisses++
|
|
88
|
+
"network-fetch-complete" -> {
|
|
89
|
+
cacheFetches++
|
|
90
|
+
cacheTotalMs += durationMs
|
|
91
|
+
cacheTopSlow.add(Pair(durationMs, filename))
|
|
92
|
+
if (cacheTopSlow.size > 5) cacheTopSlow.poll() // remove smallest
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private fun buildCacheSummaryEvent(): JSONObject? {
|
|
98
|
+
val total = cacheHits + cacheMisses
|
|
99
|
+
if (total == 0) return null
|
|
100
|
+
val hitRate = if (total > 0) (cacheHits * 100.0 / total) else 0.0
|
|
101
|
+
val avgFetchMs = if (cacheFetches > 0) cacheTotalMs / cacheFetches else 0L
|
|
102
|
+
val topSlow = JSONArray()
|
|
103
|
+
cacheTopSlow.sortedByDescending { it.first }.forEach { (ms, name) ->
|
|
104
|
+
topSlow.put(JSONObject().apply {
|
|
105
|
+
put("filename", name)
|
|
106
|
+
put("durationMs", ms)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
return JSONObject().apply {
|
|
110
|
+
put("type", "cache-summary")
|
|
111
|
+
put("hits", cacheHits)
|
|
112
|
+
put("misses", cacheMisses)
|
|
113
|
+
put("fetches", cacheFetches)
|
|
114
|
+
put("total", total)
|
|
115
|
+
put("hitRatePct", Math.round(hitRate * 10) / 10.0)
|
|
116
|
+
put("avgFetchMs", avgFetchMs)
|
|
117
|
+
put("topSlowest", topSlow)
|
|
118
|
+
put("nativeTime", SystemClock.elapsedRealtime())
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
fun configure(enabled: Boolean) {
|
|
125
|
+
profilerEnabled = BuildConfig.DEBUG && enabled
|
|
126
|
+
if (!profilerEnabled) reset()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fun isEnabled(): Boolean = profilerEnabled
|
|
130
|
+
|
|
131
|
+
/** Add a perf event to the buffer. Safe from any thread. */
|
|
132
|
+
fun add(event: JSONObject) {
|
|
133
|
+
if (!isEnabled()) return
|
|
134
|
+
if (buffer.size >= MAX_BUFFER_SIZE) {
|
|
135
|
+
buffer.poll() // drop oldest
|
|
136
|
+
}
|
|
137
|
+
// Update cache summary tally
|
|
138
|
+
val type = event.optString("type")
|
|
139
|
+
if (type.startsWith("cache-") || type == "network-fetch-complete") {
|
|
140
|
+
updateCacheSummary(type, event.optLong("durationMs", 0), event.optString("url"))
|
|
141
|
+
}
|
|
142
|
+
buffer.add(event)
|
|
143
|
+
android.util.Log.d(TAG, "[PerfBuffer] add() type=$type bufferSize=${buffer.size}")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Record start of an @JavascriptInterface bridge call.
|
|
148
|
+
* Call this immediately when the method is invoked on the JavascriptInterface.
|
|
149
|
+
*/
|
|
150
|
+
fun bridgeCallReceived(callId: String, method: String) {
|
|
151
|
+
if (!isEnabled()) return
|
|
152
|
+
pendingCalls[callId] = Pair(SystemClock.elapsedRealtime(), method)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Record the end of immediate native bridge-handler work and add an api-call event.
|
|
157
|
+
*/
|
|
158
|
+
fun bridgeCallDispatched(callId: String) {
|
|
159
|
+
if (!isEnabled()) return
|
|
160
|
+
val pending = pendingCalls.remove(callId) ?: return
|
|
161
|
+
val (t0, method) = pending
|
|
162
|
+
val t1 = SystemClock.elapsedRealtime()
|
|
163
|
+
val durationMs = t1 - t0
|
|
164
|
+
add(JSONObject().apply {
|
|
165
|
+
put("type", "api-call")
|
|
166
|
+
put("callId", callId)
|
|
167
|
+
put("method", method)
|
|
168
|
+
put("nativeStartMs", t0)
|
|
169
|
+
put("nativeEndMs", t1)
|
|
170
|
+
put("durationMs", durationMs)
|
|
171
|
+
put("thread", Thread.currentThread().name)
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Schedule a flush after onPageFinished (initial/full page load).
|
|
177
|
+
* Also starts the periodic flush loop for subsequent SPA navigations.
|
|
178
|
+
*/
|
|
179
|
+
fun scheduleFlush(webView: WebView) {
|
|
180
|
+
if (!isEnabled()) return
|
|
181
|
+
if (flushed.getAndSet(true)) {
|
|
182
|
+
android.util.Log.d(TAG, "[PerfBuffer] scheduleFlush() skipped — already flushed")
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
android.util.Log.d(TAG, "[PerfBuffer] scheduleFlush() scheduled in ${FLUSH_DELAY_MS}ms, bufferSize=${buffer.size}")
|
|
186
|
+
mainHandler.postDelayed({
|
|
187
|
+
beginFlush(webView, includeCacheSummary = true)
|
|
188
|
+
// Start periodic flush loop for SPA navigations after initial load
|
|
189
|
+
startPeriodicFlush(webView)
|
|
190
|
+
}, FLUSH_DELAY_MS)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Flush any pending events immediately (e.g. before a full URL navigation).
|
|
195
|
+
* Does NOT reset the buffer — only drains buffered events up to this point.
|
|
196
|
+
*/
|
|
197
|
+
fun flushNow(webView: WebView) {
|
|
198
|
+
if (!isEnabled()) return
|
|
199
|
+
mainHandler.post { beginFlush(webView, includeCacheSummary = false) }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Start periodic flush loop — fires every 4s and flushes only if new events exist.
|
|
204
|
+
* Call after onPageFinished so the JS receiver is registered.
|
|
205
|
+
*/
|
|
206
|
+
fun startPeriodicFlush(webView: WebView) {
|
|
207
|
+
if (!isEnabled()) return
|
|
208
|
+
mainHandler.removeCallbacks(periodicRunnable)
|
|
209
|
+
periodicWebView = webView
|
|
210
|
+
lastFlushedSize = 0
|
|
211
|
+
mainHandler.postDelayed(periodicRunnable, PERIODIC_FLUSH_INTERVAL_MS)
|
|
212
|
+
android.util.Log.d(TAG, "[PerfBuffer] startPeriodicFlush() — interval=${PERIODIC_FLUSH_INTERVAL_MS}ms")
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Stop periodic flush (call on destroy / loadUrl reset). */
|
|
216
|
+
fun stopPeriodicFlush() {
|
|
217
|
+
mainHandler.removeCallbacks(periodicRunnable)
|
|
218
|
+
periodicWebView = null
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Reset the buffer for the next page load.
|
|
223
|
+
* Call this from loadUrl so boot timing events for the new page are captured.
|
|
224
|
+
*/
|
|
225
|
+
fun reset() {
|
|
226
|
+
stopPeriodicFlush()
|
|
227
|
+
buffer.clear()
|
|
228
|
+
deliveryState.reset()
|
|
229
|
+
pendingCalls.clear()
|
|
230
|
+
flushed.set(false)
|
|
231
|
+
lastFlushedSize = 0
|
|
232
|
+
cacheHits = 0
|
|
233
|
+
cacheMisses = 0
|
|
234
|
+
cacheFetches = 0
|
|
235
|
+
cacheTotalMs = 0L
|
|
236
|
+
cacheTopSlow.clear()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ─── Flush ───────────────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
/** Moves the current queue into an immutable in-flight batch until JS acknowledges it. */
|
|
242
|
+
private fun beginFlush(webView: WebView, includeCacheSummary: Boolean) {
|
|
243
|
+
if (!isEnabled()) return
|
|
244
|
+
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
245
|
+
mainHandler.post { beginFlush(webView, includeCacheSummary) }
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
if (deliveryState.hasInFlight()) return
|
|
249
|
+
val events = JSONArray()
|
|
250
|
+
while (buffer.isNotEmpty()) {
|
|
251
|
+
buffer.poll()?.let { events.put(it) }
|
|
252
|
+
}
|
|
253
|
+
if (includeCacheSummary) buildCacheSummaryEvent()?.let { events.put(it) }
|
|
254
|
+
if (events.length() == 0) return
|
|
255
|
+
|
|
256
|
+
val batch = deliveryState.begin(events) ?: return
|
|
257
|
+
deliver(webView, batch)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private fun deliver(webView: WebView, batch: PerfDeliveryState.Batch<JSONArray>) {
|
|
261
|
+
if (!isEnabled() || !deliveryState.isCurrent(batch)) return
|
|
262
|
+
try {
|
|
263
|
+
val json = batch.payload.toString().replace("\\", "\\\\").replace("'", "\\'")
|
|
264
|
+
val batchId = JSONObject.quote(batch.id)
|
|
265
|
+
webView.evaluateJavascript(
|
|
266
|
+
"window.__catalystPerfBatch ? window.__catalystPerfBatch($batchId, '$json') : false",
|
|
267
|
+
) { result ->
|
|
268
|
+
if (result == "true") acknowledge(batch) else retry(webView, batch)
|
|
269
|
+
}
|
|
270
|
+
} catch (_: Exception) {
|
|
271
|
+
retry(webView, batch)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private fun acknowledge(batch: PerfDeliveryState.Batch<JSONArray>) {
|
|
276
|
+
if (deliveryState.acknowledge(batch)) {
|
|
277
|
+
lastFlushedSize = 0
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private fun retry(webView: WebView, batch: PerfDeliveryState.Batch<JSONArray>) {
|
|
282
|
+
val retry = deliveryState.retry(batch, MAX_FLUSH_RETRIES)
|
|
283
|
+
if (retry == null) {
|
|
284
|
+
android.util.Log.w(TAG, "[PerfBuffer] batch ${batch.id} was not acknowledged; retaining until reset")
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
mainHandler.postDelayed({ deliver(webView, retry) }, FLUSH_RETRY_DELAY_MS)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package io.yourname.androidproject.utils
|
|
2
|
+
|
|
3
|
+
internal class ProfilerNavigationState {
|
|
4
|
+
private var isInitialNavigation = true
|
|
5
|
+
|
|
6
|
+
data class Navigation(val url: String, val shouldReset: Boolean)
|
|
7
|
+
|
|
8
|
+
fun begin(url: String): Navigation {
|
|
9
|
+
val shouldReset = !isInitialNavigation
|
|
10
|
+
isInitialNavigation = false
|
|
11
|
+
return Navigation(url, shouldReset)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package io.yourname.androidproject
|
|
2
|
+
|
|
3
|
+
import io.yourname.androidproject.utils.PerfDeliveryState
|
|
4
|
+
import io.yourname.androidproject.utils.ProfilerNavigationState
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertFalse
|
|
7
|
+
import org.junit.Assert.assertNotNull
|
|
8
|
+
import org.junit.Assert.assertNull
|
|
9
|
+
import org.junit.Assert.assertTrue
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
|
|
12
|
+
class ProfilerLifecycleTest {
|
|
13
|
+
@Test
|
|
14
|
+
fun `offline startup and retry navigation record separate page lifecycles`() {
|
|
15
|
+
val state = ProfilerNavigationState()
|
|
16
|
+
val offlineStart = state.begin("file:///android_asset/offline/offline.html")
|
|
17
|
+
val retry = state.begin("https://example.com")
|
|
18
|
+
|
|
19
|
+
assertFalse(offlineStart.shouldReset)
|
|
20
|
+
assertEquals("file:///android_asset/offline/offline.html", offlineStart.url)
|
|
21
|
+
assertTrue(retry.shouldReset)
|
|
22
|
+
assertEquals("https://example.com", retry.url)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Test
|
|
26
|
+
fun `batch remains in flight until acknowledged and retries retain its id`() {
|
|
27
|
+
val state = PerfDeliveryState<List<String>>()
|
|
28
|
+
val batch = state.begin(listOf("boot-activity-created"))
|
|
29
|
+
|
|
30
|
+
assertNotNull(batch)
|
|
31
|
+
assertNull(state.begin(listOf("boot-page-started")))
|
|
32
|
+
|
|
33
|
+
val retry = state.retry(batch!!, maxRetries = 2)
|
|
34
|
+
assertNotNull(retry)
|
|
35
|
+
assertEquals(batch.id, retry!!.id)
|
|
36
|
+
assertEquals(1, retry.retryCount)
|
|
37
|
+
assertTrue(state.acknowledge(retry))
|
|
38
|
+
assertNotNull(state.begin(listOf("boot-page-started")))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `reset invalidates stale delivery callbacks`() {
|
|
43
|
+
val state = PerfDeliveryState<List<String>>()
|
|
44
|
+
val batch = state.begin(listOf("boot-load-url"))!!
|
|
45
|
+
|
|
46
|
+
state.reset()
|
|
47
|
+
|
|
48
|
+
assertFalse(state.acknowledge(batch))
|
|
49
|
+
assertNull(state.retry(batch, maxRetries = 2))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,9 +1,301 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
4
|
+
var _path = _interopRequireDefault(require("path"));
|
|
5
|
+
var _child_process = require("child_process");
|
|
6
|
+
var _utils = require("./utils.js");
|
|
7
|
+
var _TerminalProgress = _interopRequireDefault(require("./TerminalProgress.js"));
|
|
8
|
+
var _setupServer = require("./setupServer.js");
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
const catalystCorePath = _path.default.dirname(require.resolve("catalyst-core/package.json"));
|
|
11
|
+
const pwd = _path.default.join(catalystCorePath, "dist/native");
|
|
12
|
+
const configPath = `${process.env.PWD}/config/config.json`;
|
|
13
|
+
const steps = {
|
|
14
|
+
java: "Check Java Environment",
|
|
15
|
+
config: "Initialize Configuration",
|
|
16
|
+
androidTools: "Validate Android Tools",
|
|
17
|
+
emulator: "Configure Android Emulator",
|
|
18
|
+
properties: "Update Local Properties",
|
|
19
|
+
saveConfig: "Save Configuration",
|
|
20
|
+
setupServer: "Setup Server"
|
|
21
|
+
};
|
|
22
|
+
const progressPaddingConfig = {
|
|
23
|
+
titlePaddingTop: 2,
|
|
24
|
+
titlePaddingBottom: 1,
|
|
25
|
+
stepPaddingLeft: 4,
|
|
26
|
+
stepSpacing: 1,
|
|
27
|
+
errorPaddingLeft: 6,
|
|
28
|
+
bottomMargin: 2
|
|
29
|
+
};
|
|
30
|
+
const progress = new _TerminalProgress.default(steps, "Catalyst Universal Android Setup", progressPaddingConfig);
|
|
31
|
+
async function checkJavaInstallation() {
|
|
32
|
+
try {
|
|
33
|
+
(0, _utils.runCommand)("java -version");
|
|
34
|
+
(0, _utils.runCommand)("javac -version");
|
|
35
|
+
progress.log("Java is installed and configured", "success");
|
|
36
|
+
return true;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
progress.log("Java installation not found or not properly configured", "error");
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function installJava() {
|
|
43
|
+
progress.log("Installing Java (Zulu JDK 17)...");
|
|
44
|
+
try {
|
|
45
|
+
try {
|
|
46
|
+
(0, _utils.runCommand)("brew --version");
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error("Homebrew is required but not installed. Please install Homebrew first.");
|
|
49
|
+
}
|
|
50
|
+
const javaHome = "/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home";
|
|
51
|
+
|
|
52
|
+
// Try installing Java
|
|
53
|
+
try {
|
|
54
|
+
await (0, _utils.runCommand)("brew install --cask zulu@17");
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (error.message.includes("already installed")) {
|
|
57
|
+
progress.log("Attempting to reinstall Java...", "info");
|
|
58
|
+
try {
|
|
59
|
+
await (0, _utils.runCommand)("brew reinstall --cask zulu@17");
|
|
60
|
+
} catch (reinstallError) {
|
|
61
|
+
throw new Error(`Failed to reinstall Java: ${reinstallError.message}. Please run 'brew reinstall --cask zulu@17' manually.`);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Verify Java installation
|
|
69
|
+
if (!_fs.default.existsSync(javaHome)) {
|
|
70
|
+
throw new Error(`Java installation not found at ${javaHome}. Please verify the installation manually.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Set up environment variables
|
|
74
|
+
const homeDir = process.env.HOME;
|
|
75
|
+
const shellProfile = `${homeDir}/.zshrc`;
|
|
76
|
+
const exportCommand = `\n# Java Configuration\nexport JAVA_HOME=${javaHome}\nexport PATH=$JAVA_HOME/bin:$PATH\n`;
|
|
77
|
+
try {
|
|
78
|
+
_fs.default.appendFileSync(shellProfile, exportCommand);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(`Failed to update ${shellProfile}: ${error.message}. Please add the following lines manually:\n${exportCommand}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Set for current process
|
|
84
|
+
process.env.JAVA_HOME = javaHome;
|
|
85
|
+
process.env.PATH = `${javaHome}/bin:${process.env.PATH}`;
|
|
86
|
+
|
|
87
|
+
// Verify Java configuration
|
|
88
|
+
try {
|
|
89
|
+
const javaVersion = (0, _utils.runCommand)("java -version");
|
|
90
|
+
progress.log("Java installed and configured successfully", "success");
|
|
91
|
+
progress.log(`Java Version: ${javaVersion}`, "info");
|
|
92
|
+
progress.log(`IMPORTANT: Please run the following commands to complete setup:`, "warning");
|
|
93
|
+
progress.log(`1. source ${shellProfile}`, "info");
|
|
94
|
+
progress.log(`2. echo $JAVA_HOME`, "info");
|
|
95
|
+
return true;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new Error(`Java installation verified but environment not configured. Please run:\n1. source ${shellProfile}\n2. echo $JAVA_HOME`);
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
progress.log(error.message, "error");
|
|
101
|
+
progress.log("Manual Setup Instructions:", "info");
|
|
102
|
+
progress.log("1. Run: brew reinstall --cask zulu@17", "info");
|
|
103
|
+
progress.log("2. Add to ~/.zshrc:", "info");
|
|
104
|
+
progress.log(" export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home", "info");
|
|
105
|
+
progress.log(" export PATH=$JAVA_HOME/bin:$PATH", "info");
|
|
106
|
+
progress.log("3. Run: source ~/.zshrc", "info");
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function validateJavaEnvironment() {
|
|
111
|
+
progress.start("java");
|
|
112
|
+
const hasJava = await checkJavaInstallation();
|
|
113
|
+
if (!hasJava) {
|
|
114
|
+
progress.log("Java installation required. Starting installation process...");
|
|
115
|
+
const javaInstalled = await installJava();
|
|
116
|
+
if (!javaInstalled) {
|
|
117
|
+
progress.fail("java", "Failed to set up Java environment");
|
|
118
|
+
throw new Error("Failed to set up Java environment");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
progress.complete("java");
|
|
122
|
+
}
|
|
123
|
+
async function initializeConfig() {
|
|
124
|
+
const configFile = _fs.default.readFileSync(configPath, "utf8");
|
|
125
|
+
const config = JSON.parse(configFile);
|
|
126
|
+
const {
|
|
127
|
+
WEBVIEW_CONFIG
|
|
128
|
+
} = config;
|
|
129
|
+
if (!WEBVIEW_CONFIG || Object.keys(WEBVIEW_CONFIG).length === 0) {
|
|
130
|
+
progress.log("WebView Config missing in " + configPath, "error");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
if (!WEBVIEW_CONFIG.android) {
|
|
134
|
+
WEBVIEW_CONFIG.android = {};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
WEBVIEW_CONFIG
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function saveConfig(newConfig) {
|
|
141
|
+
try {
|
|
142
|
+
const existingConfigFile = _fs.default.readFileSync(configPath, "utf8");
|
|
143
|
+
const existingConfig = JSON.parse(existingConfigFile);
|
|
144
|
+
const updatedConfig = {
|
|
145
|
+
...existingConfig,
|
|
146
|
+
WEBVIEW_CONFIG: {
|
|
147
|
+
...existingConfig.WEBVIEW_CONFIG,
|
|
148
|
+
android: newConfig.WEBVIEW_CONFIG.android
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
_fs.default.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2));
|
|
152
|
+
progress.log("Configuration saved successfully", "success");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
progress.log("Failed to save configuration: " + error.message, "error");
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function validateAndroidTools(androidConfig) {
|
|
159
|
+
const ANDROID_SDK = androidConfig.sdkPath;
|
|
160
|
+
const EMULATOR_PATH = `${ANDROID_SDK}/emulator/emulator`;
|
|
161
|
+
const ADB_PATH = `${ANDROID_SDK}/platform-tools/adb`;
|
|
162
|
+
if (!ANDROID_SDK) {
|
|
163
|
+
throw new Error("Android SDK path is not configured");
|
|
164
|
+
}
|
|
165
|
+
if (!_fs.default.existsSync(ANDROID_SDK)) {
|
|
166
|
+
throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);
|
|
167
|
+
}
|
|
168
|
+
if (!_fs.default.existsSync(ADB_PATH)) {
|
|
169
|
+
throw new Error(`ADB not found at: ${ADB_PATH}`);
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
(0, _utils.runCommand)(`${ADB_PATH} version`);
|
|
173
|
+
progress.log("ADB is valid", "success");
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error(`ADB is not working properly: ${error.message}`);
|
|
176
|
+
}
|
|
177
|
+
if (!_fs.default.existsSync(EMULATOR_PATH)) {
|
|
178
|
+
throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
(0, _utils.runCommand)(`${EMULATOR_PATH} -version`);
|
|
182
|
+
progress.log("Emulator is valid", "success");
|
|
183
|
+
} catch (error) {
|
|
184
|
+
throw new Error(`Emulator is not working properly: ${error.message}`);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const avdOutput = (0, _utils.runCommand)(`${EMULATOR_PATH} -list-avds`);
|
|
188
|
+
if (!avdOutput.includes(androidConfig.emulatorName)) {
|
|
189
|
+
throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);
|
|
190
|
+
}
|
|
191
|
+
progress.log(`Emulator "${androidConfig.emulatorName}" exists`, "success");
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new Error(`Error checking emulator AVD: ${error.message}`);
|
|
194
|
+
}
|
|
195
|
+
progress.log("Android tools validation completed successfully!", "success");
|
|
196
|
+
return {
|
|
197
|
+
EMULATOR_PATH,
|
|
198
|
+
ADB_PATH
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function checkEmulator(ADB_PATH) {
|
|
202
|
+
try {
|
|
203
|
+
const devices = (0, _utils.runCommand)(`${ADB_PATH} devices`);
|
|
204
|
+
return devices.includes("emulator");
|
|
205
|
+
} catch (error) {
|
|
206
|
+
progress.log(`Error checking emulator: ${error}`, "error");
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function startEmulator(EMULATOR_PATH, androidConfig) {
|
|
211
|
+
progress.log(`Starting emulator: ${androidConfig.emulatorName}...`);
|
|
212
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process - EMULATOR_PATH is derived from SDK configuration and emulatorName is local native setup config.
|
|
213
|
+
// eslint-disable-next-line security/detect-child-process
|
|
214
|
+
(0, _child_process.exec)(`${EMULATOR_PATH} -avd ${androidConfig.emulatorName} -read-only > /dev/null &`, error => {
|
|
215
|
+
if (error) {
|
|
216
|
+
progress.log(`Error starting emulator: ${error}`, "error");
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
async function updateLocalProperties(sdkPath) {
|
|
221
|
+
progress.start("properties");
|
|
222
|
+
try {
|
|
223
|
+
(0, _utils.runCommand)(`cd ${pwd}/androidProject && ./gradlew updateSdkPath -PsdkPath="${sdkPath}"`);
|
|
224
|
+
progress.log("Updated local.properties successfully", "success");
|
|
225
|
+
progress.complete("properties");
|
|
226
|
+
} catch (error) {
|
|
227
|
+
progress.fail("properties", `Failed to update local.properties: ${error.message}`);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async function setupAndroidEnvironment() {
|
|
232
|
+
try {
|
|
233
|
+
await validateJavaEnvironment();
|
|
234
|
+
progress.start("config");
|
|
235
|
+
await initializeConfig();
|
|
236
|
+
const config = await (0, _utils.validateAndCompleteConfig)("android", configPath);
|
|
237
|
+
progress.complete("config");
|
|
238
|
+
progress.start("androidTools");
|
|
239
|
+
const {
|
|
240
|
+
EMULATOR_PATH,
|
|
241
|
+
ADB_PATH
|
|
242
|
+
} = validateAndroidTools(config.android);
|
|
243
|
+
progress.complete("androidTools");
|
|
244
|
+
progress.start("emulator");
|
|
245
|
+
const emulatorRunning = await checkEmulator(ADB_PATH);
|
|
246
|
+
if (!emulatorRunning) {
|
|
247
|
+
progress.log("No emulator running, attempting to start one...");
|
|
248
|
+
await startEmulator(EMULATOR_PATH, config.android);
|
|
249
|
+
} else {
|
|
250
|
+
progress.log("Emulator already running, proceeding with installation...", "success");
|
|
251
|
+
}
|
|
252
|
+
progress.complete("emulator");
|
|
253
|
+
if (config.android?.sdkPath) {
|
|
254
|
+
await updateLocalProperties(config.android.sdkPath);
|
|
255
|
+
}
|
|
256
|
+
progress.start("saveConfig");
|
|
257
|
+
await saveConfig({
|
|
258
|
+
WEBVIEW_CONFIG: config
|
|
259
|
+
});
|
|
260
|
+
progress.complete("saveConfig");
|
|
261
|
+
progress.start("setupServer");
|
|
262
|
+
await (0, _setupServer.setupServer)(configPath);
|
|
263
|
+
progress.complete("setupServer");
|
|
264
|
+
progress.printTreeContent("Configuration Explanation", ["WEBVIEW_CONFIG: Main configuration object for the WebView setup", {
|
|
265
|
+
text: "port: Port number for the WebView server",
|
|
266
|
+
indent: 1,
|
|
267
|
+
prefix: "├─ ",
|
|
268
|
+
color: "gray"
|
|
269
|
+
}, {
|
|
270
|
+
text: "android: Android-specific configuration",
|
|
271
|
+
indent: 1,
|
|
272
|
+
prefix: "└─ ",
|
|
273
|
+
color: "gray"
|
|
274
|
+
}, {
|
|
275
|
+
text: "buildType: Build type (debug/release)",
|
|
276
|
+
indent: 2,
|
|
277
|
+
prefix: "├─ ",
|
|
278
|
+
color: "gray"
|
|
279
|
+
}, {
|
|
280
|
+
text: "sdkPath: Android SDK path",
|
|
281
|
+
indent: 2,
|
|
282
|
+
prefix: "├─ ",
|
|
283
|
+
color: "gray"
|
|
284
|
+
}, {
|
|
285
|
+
text: "emulatorName: Selected Android emulator name",
|
|
286
|
+
indent: 2,
|
|
287
|
+
prefix: "└─ ",
|
|
288
|
+
color: "gray"
|
|
289
|
+
}]);
|
|
290
|
+
progress.printTreeContent("Final Configuration", [JSON.stringify(config, null, 2)]);
|
|
291
|
+
process.exit(0);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (progress.currentStep) {
|
|
294
|
+
progress.fail(progress.currentStep.id, error.message);
|
|
295
|
+
}
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Execute the main setup
|
|
9
301
|
setupAndroidEnvironment();
|