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
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ presets: [
3
+ ["@babel/preset-env", { targets: { node: "current" } }],
4
+ ["@babel/preset-react", { runtime: "automatic" }],
5
+ ["@babel/preset-typescript", { isTSX: true, allExtensions: true }],
6
+ ],
7
+ compact: false,
8
+ }
package/bin/catalyst.js CHANGED
@@ -1,128 +1,72 @@
1
1
  #!/usr/bin/env node
2
- "use strict"
3
- process.on("unhandledRejection", (err) => {
4
- throw err
5
- })
6
- const { spawnSync } = require("node:child_process")
7
- const args = process.argv.slice(2)
8
2
 
9
- // Array of valid commands
10
- const validCommands = [
11
- "build",
12
- "start",
13
- "serve",
14
- "devBuild",
15
- "devServe",
16
- "plugin",
17
- "plugins",
18
- "buildApp",
19
- "buildApp:ios",
20
- "buildApp:android",
21
- "setupEmulator",
22
- "setupEmulator:ios",
23
- "setupEmulator:android",
24
- ]
3
+ import { spawnSync } from "node:child_process"
4
+ import { dirname, resolve } from "node:path"
5
+ import { fileURLToPath } from "node:url"
6
+
7
+ process.on("unhandledRejection", (error) => {
8
+ throw error
9
+ })
25
10
 
26
- // Map of platform-specific commands to their script paths
11
+ const __dirname = dirname(fileURLToPath(import.meta.url))
12
+ const args = process.argv.slice(2)
13
+ const webCommands = new Set(["build", "start", "serve", "serve:inspect", "plugin", "plugins"])
27
14
  const platformScripts = {
28
15
  "setupEmulator:ios": "../dist/native/setupEmulatorIos.js",
29
16
  "setupEmulator:android": "../dist/native/androidSetup.js",
30
17
  "buildApp:ios": "../dist/native/buildAppIos.js",
31
18
  "buildApp:android": "../dist/native/buildAppAndroid.js",
32
19
  }
20
+ const validCommands = new Set([...webCommands, "buildApp", "setupEmulator", ...Object.keys(platformScripts)])
21
+
22
+ const scriptIndex = args.findIndex((arg) => validCommands.has(arg))
23
+ const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
24
+ const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
25
+ const scriptArgs = args.slice(scriptIndex + 1)
33
26
 
34
- // Helper to check if arg is a platform command
35
- const isPlatformCommand = (arg, prefix) => {
36
- if (!arg.startsWith(`${prefix}:`)) return false
37
- const platform = arg.split(":")[1]
38
- return ["ios", "android"].includes(platform) || platform === undefined
27
+ function handleProcessResult(result) {
28
+ if (result.signal) {
29
+ if (result.signal === "SIGKILL") {
30
+ console.error("The Catalyst process was terminated with SIGKILL.")
31
+ } else if (result.signal === "SIGTERM") {
32
+ console.error("The Catalyst process was terminated with SIGTERM.")
33
+ }
34
+ process.exit(1)
35
+ }
36
+ process.exit(result.status ?? 1)
39
37
  }
40
38
 
41
- // Helper function to run a platform command
42
- const runPlatformCommand = (baseCommand, platform) => {
43
- const command = `${baseCommand}:${platform}`
44
- const result = spawnSync(
39
+ function runScript(relativePath, extraArgs = []) {
40
+ return spawnSync(
45
41
  process.execPath,
46
- nodeArgs.concat(require.resolve(platformScripts[command])).concat(args.slice(scriptIndex + 1)),
47
- { stdio: "inherit" }
42
+ [...nodeArgs, resolve(__dirname, relativePath), ...extraArgs, ...scriptArgs],
43
+ {
44
+ stdio: "inherit",
45
+ }
48
46
  )
49
- return result
50
47
  }
51
48
 
52
- // Helper function to run commands for all platforms
53
- const runAllPlatforms = (baseCommand) => {
54
- const platforms = ["ios", "android"]
55
- for (const platform of platforms) {
56
- const result = runPlatformCommand(baseCommand, platform)
57
- if (result.status !== 0) {
58
- handleProcessResult(result)
59
- return
60
- }
49
+ function runPlatformCommand(command) {
50
+ return runScript(platformScripts[command])
51
+ }
52
+
53
+ if (script === "buildApp" || script === "setupEmulator") {
54
+ for (const platform of ["ios", "android"]) {
55
+ const result = runPlatformCommand(`${script}:${platform}`)
56
+ if (result.status !== 0 || result.signal) handleProcessResult(result)
61
57
  }
62
58
  process.exit(0)
63
59
  }
64
60
 
65
- const scriptIndex = args.findIndex(
66
- (x) =>
67
- x === "build" ||
68
- x === "start" ||
69
- x === "serve" ||
70
- x === "devBuild" ||
71
- x === "devServe" ||
72
- x === "plugin" ||
73
- x === "plugins" ||
74
- isPlatformCommand(x, "buildApp") ||
75
- isPlatformCommand(x, "setupEmulator")
76
- )
77
- const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
78
- const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
79
- const resolvedScript = script === "plugin" ? "plugins" : script
80
-
81
- if (validCommands.includes(script)) {
82
- // Handle platform-specific or combined commands
83
- if (script === "buildApp" || script === "setupEmulator") {
84
- // Run for all platforms if no specific platform is specified
85
- runAllPlatforms(script)
86
- } else if (script in platformScripts) {
87
- // Run for specific platform
88
- const result = spawnSync(
89
- process.execPath,
90
- nodeArgs.concat(require.resolve(platformScripts[script])).concat(args.slice(scriptIndex + 1)),
91
- { stdio: "inherit" }
92
- )
93
- handleProcessResult(result)
94
- } else {
95
- // Original commands
96
- const result = spawnSync(
97
- process.execPath,
98
- nodeArgs
99
- .concat(require.resolve("../dist/scripts/" + resolvedScript))
100
- .concat(args.slice(scriptIndex + 1)),
101
- { stdio: "inherit" }
102
- )
103
- handleProcessResult(result)
104
- }
105
- } else {
106
- console.log('Unknown script "' + script + '".')
61
+ if (platformScripts[script]) {
62
+ handleProcessResult(runPlatformCommand(script))
107
63
  }
108
64
 
109
- // Helper function to handle process results
110
- function handleProcessResult(result) {
111
- if (result.signal) {
112
- if (result.signal === "SIGKILL") {
113
- console.log(
114
- "The build failed because the process exited too early. " +
115
- "This probably means the system ran out of memory or someone called " +
116
- "`kill -9` on the process."
117
- )
118
- } else if (result.signal === "SIGTERM") {
119
- console.log(
120
- "The build failed because the process exited too early. " +
121
- "Someone might have called `kill` or `killall`, or the system could " +
122
- "be shutting down."
123
- )
124
- }
125
- process.exit(1)
126
- }
127
- process.exit(result.status)
65
+ if (webCommands.has(script)) {
66
+ const scriptName = script === "plugin" ? "plugins" : script === "serve:inspect" ? "serve" : script
67
+ const extraArgs = script === "serve:inspect" ? ["--inspect"] : []
68
+ handleProcessResult(runScript(`../dist/scripts/${scriptName}.js`, extraArgs))
128
69
  }
70
+
71
+ console.error(`Unknown script "${script}".`)
72
+ process.exit(1)
package/changelog.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 1.0.0-beta.1
2
+
3
+ - Replaced the Webpack build and SSR pipeline with Vite and ESM.
4
+ - Added React 19 Suspense-aware routing, split components, and manifest-based asset loading.
5
+ - Added opt-in request, compression, flush, and bot-aware OpenTelemetry spans.
6
+ - Preserved native application builds, internal plugins, Sentry exports, and safe-area SSR hydration.
7
+ - Removed the legacy `devBuild` and `devServe` commands.
8
+
1
9
  # Changelog
2
10
 
3
11
  ## [0.1.0-beta.2] - 2026-05-06
package/dist/caching.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});var _apiCaching=require("@tata1mg/api-caching");Object.keys(_apiCaching).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_apiCaching[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _apiCaching[key];}});});
1
+ export * from "@tata1mg/api-caching"
@@ -0,0 +1,7 @@
1
+ // Injected by webpack DefinePlugin in base.babel.js at build time.
2
+ // Reflects which @catalyst/* packages are present in the app's node_modules.
3
+ declare const __CATALYST_PACKAGES__: {
4
+ readonly ai: boolean
5
+ readonly webAILocal: boolean
6
+ readonly nativeAILocal: boolean
7
+ }
package/dist/index.jsx ADDED
@@ -0,0 +1,8 @@
1
+ export * from "react-router-dom"
2
+ export * from "./web-router/components/RouterDataProvider.jsx"
3
+ export * from "./web-router/components/MetaTag.jsx"
4
+ export { split, hydrationReady } from "./web-router/components/Split.jsx"
5
+ export { split as default } from "./web-router/components/Split.jsx"
6
+ export * from "./web-router/hooks.jsx"
7
+ export * from "./web-router/utils/metaDataUtils.jsx"
8
+ export * from "./server/renderer/document/index.jsx"
package/dist/logger.js CHANGED
@@ -1,4 +1,10 @@
1
- "use strict";const pc=require("picocolors");const winston=require("winston");const DailyRotateFile=require("winston-daily-rotate-file");const{createLogger,format,transports}=winston;/**
1
+ import pc from "picocolors"
2
+ import winston from "winston"
3
+ import DailyRotateFile from "winston-daily-rotate-file"
4
+
5
+ const { createLogger, format, transports } = winston
6
+
7
+ /**
2
8
  * @description Logger library with rotational strategy. Creates a logs folder in root.
3
9
  * With debug, error and info log directories with their respective log files.
4
10
  *
@@ -6,5 +12,70 @@
6
12
  * @param config { @enableDebugLogs: Bool // default: true }
7
13
  * @returns loggerInstance
8
14
  *
9
- */const configureLogger=(config={})=>{const{enableDebugLogs=true,enableFileLogging=true,enableConsoleLogging=true}=config;const consoleTransport=new transports.Console({level:"debug"});const fileTransport=(type="info")=>{return new DailyRotateFile({filename:`${process.env.src_path}/logs/${type}/%DATE%.${type}.log`,datePattern:"YYYY-MM-DD",maxFiles:"3d",// Logs will be removed after 2days,
10
- loglevel:type,level:type});};const infoLogger=createLogger({format:format.combine(format.timestamp({format:"YYYY-MM-DD HH:mm:ss"}),format.json()),defaultMeta:{loglevel:"info"}});const debugLogger=createLogger({format:format.combine(format.timestamp({format:"YYYY-MM-DD HH:mm:ss"}),format.json()),defaultMeta:{loglevel:"debug"}});const errorLogger=createLogger({format:format.combine(format.timestamp({format:"YYYY-MM-DD HH:mm:ss"}),format.json()),defaultMeta:{loglevel:"error"}});if(enableConsoleLogging&&JSON.parse(enableConsoleLogging)){infoLogger.add(consoleTransport);debugLogger.add(consoleTransport);errorLogger.add(consoleTransport);}if(enableFileLogging&&JSON.parse(enableFileLogging)){infoLogger.add(fileTransport("info"));debugLogger.add(fileTransport("debug"));errorLogger.add(fileTransport("error"));}const Logger={debug:()=>{},error:msg=>{console.log(pc.red(pc.bold("ERROR: "+JSON.stringify(msg))));errorLogger.error(msg);},info:msg=>{console.log(pc.green(pc.bold("INFO: "+JSON.stringify(msg))));infoLogger.info(msg);}};if(enableDebugLogs&&JSON.parse(enableDebugLogs)){Logger.debug=msg=>{console.log(pc.yellow(pc.bold("DEBUG: "+JSON.stringify(msg))));debugLogger.debug(msg);};}if(global)global.logger=Logger;return Logger;};module.exports={configureLogger};
15
+ */
16
+ const configureLogger = (config = {}) => {
17
+ const { enableDebugLogs = true, enableFileLogging = true, enableConsoleLogging = true } = config
18
+
19
+ const consoleTransport = new transports.Console({ level: "debug" })
20
+
21
+ const fileTransport = (type = "info") => {
22
+ return new DailyRotateFile({
23
+ filename: `${process.env.src_path}/logs/${type}/%DATE%.${type}.log`,
24
+ datePattern: "YYYY-MM-DD",
25
+ maxFiles: "3d", // Logs will be removed after 2days,
26
+ loglevel: type,
27
+ level: type,
28
+ })
29
+ }
30
+
31
+ const infoLogger = createLogger({
32
+ format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
33
+ defaultMeta: { loglevel: "info" },
34
+ })
35
+
36
+ const debugLogger = createLogger({
37
+ format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
38
+ defaultMeta: { loglevel: "debug" },
39
+ })
40
+
41
+ const errorLogger = createLogger({
42
+ format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
43
+ defaultMeta: { loglevel: "error" },
44
+ })
45
+
46
+ if (enableConsoleLogging && JSON.parse(enableConsoleLogging)) {
47
+ infoLogger.add(consoleTransport)
48
+ debugLogger.add(consoleTransport)
49
+ errorLogger.add(consoleTransport)
50
+ }
51
+
52
+ if (enableFileLogging && JSON.parse(enableFileLogging)) {
53
+ infoLogger.add(fileTransport("info"))
54
+ debugLogger.add(fileTransport("debug"))
55
+ errorLogger.add(fileTransport("error"))
56
+ }
57
+
58
+ const Logger = {
59
+ debug: () => {},
60
+ error: (msg) => {
61
+ console.log(pc.red(pc.bold("ERROR: " + JSON.stringify(msg))))
62
+ errorLogger.error(msg)
63
+ },
64
+ info: (msg) => {
65
+ console.log(pc.green(pc.bold("INFO: " + JSON.stringify(msg))))
66
+ infoLogger.info(msg)
67
+ },
68
+ }
69
+
70
+ if (enableDebugLogs && JSON.parse(enableDebugLogs)) {
71
+ Logger.debug = (msg) => {
72
+ console.log(pc.yellow(pc.bold("DEBUG: " + JSON.stringify(msg))))
73
+ debugLogger.debug(msg)
74
+ }
75
+ }
76
+
77
+ if (global) global.logger = Logger
78
+ return Logger
79
+ }
80
+
81
+ export { configureLogger }
@@ -60,6 +60,22 @@ fun isNotificationsEnabled(): Boolean {
60
60
  }
61
61
  }
62
62
 
63
+ fun isAIEnabled(): Boolean {
64
+ return try {
65
+ if (configPath == null) return false
66
+ val configFile = File(configPath!!)
67
+ if (!configFile.exists()) return false
68
+
69
+ val json = JSONObject(configFile.readText())
70
+ if (!json.has("WEBVIEW_CONFIG")) return false
71
+
72
+ val webviewConfig = json.getJSONObject("WEBVIEW_CONFIG")
73
+ webviewConfig.optJSONObject("ai")?.optBoolean("enabled", false) ?: false
74
+ } catch (e: Exception) {
75
+ false
76
+ }
77
+ }
78
+
63
79
  fun getLocalIpAddress(): String {
64
80
  return NetworkInterface.getNetworkInterfaces().toList()
65
81
  .flatMap { it.inetAddresses.toList() }
@@ -151,6 +167,7 @@ android {
151
167
 
152
168
  kotlinOptions {
153
169
  jvmTarget = "11"
170
+ freeCompilerArgs += "-Xskip-metadata-version-check"
154
171
  }
155
172
 
156
173
  buildFeatures {
@@ -168,8 +185,9 @@ android {
168
185
  excludes.add("META-INF/io.netty.versions.properties")
169
186
  }
170
187
  jniLibs {
171
- // Enable 16KB page size alignment for all native libraries
172
- useLegacyPackaging = false
188
+ // libLiteRtClGlAccelerator.so must be extracted to disk for GPU dlopen().
189
+ // This must live in the app module — AGP ignores useLegacyPackaging in library modules.
190
+ useLegacyPackaging = isAIEnabled()
173
191
  }
174
192
  }
175
193
 
@@ -186,7 +204,7 @@ android {
186
204
  }
187
205
  }
188
206
 
189
- // Conditional source sets based on notifications config
207
+ // Conditional source sets based on feature config
190
208
  sourceSets {
191
209
  getByName("main") {
192
210
  if (isNotificationsEnabled()) {
@@ -196,6 +214,20 @@ android {
196
214
  logger.info("SourceSet selected: noFcm (notifications disabled)")
197
215
  java.srcDirs("src/noFcm/java")
198
216
  }
217
+ // AI has no source-set swap — CatalystAIBridge self-registers via ServiceLoader
218
+ // when @catalyst/cloud-ai is on the classpath.
219
+ }
220
+ }
221
+ }
222
+
223
+ configurations.all {
224
+ resolutionStrategy.eachDependency {
225
+ if (requested.group == "org.jetbrains.kotlin" &&
226
+ (requested.name == "kotlin-stdlib" ||
227
+ requested.name == "kotlin-stdlib-jdk7" ||
228
+ requested.name == "kotlin-stdlib-jdk8" ||
229
+ requested.name == "kotlin-reflect")) {
230
+ useVersion("2.0.21")
199
231
  }
200
232
  }
201
233
  }
@@ -217,7 +249,16 @@ dependencies {
217
249
  implementation("io.ktor:ktor-server-core:3.0.3")
218
250
  implementation("io.ktor:ktor-server-netty:3.0.3")
219
251
  implementation("io.ktor:ktor-server-content-negotiation:3.0.3")
220
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
252
+ implementation("io.ktor:ktor-server-sse:3.0.3")
253
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
254
+
255
+ // Native AI — compileOnly always (for types), implementation only when ai.enabled=true (for runtime bundling)
256
+ if (project.findProject(":catalyst-cloud-ai") != null) {
257
+ compileOnly(project(":catalyst-cloud-ai"))
258
+ if (isAIEnabled()) {
259
+ implementation(project(":catalyst-cloud-ai"))
260
+ }
261
+ }
221
262
 
222
263
  // Security detection dependencies
223
264
  implementation("com.scottyab:rootbeer-lib:0.1.1") // Root detection
@@ -16,12 +16,15 @@ import androidx.credentials.exceptions.GetCredentialCancellationException
16
16
  import androidx.credentials.exceptions.GetCredentialException
17
17
  import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
18
18
  import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
19
+ // LiteRT imports removed — AI logic lives in catalyst-ai (CatalystAIBridge plugin)
19
20
  import io.yourname.androidproject.MainActivity
20
21
  import io.yourname.androidproject.utils.*
21
22
  import kotlinx.coroutines.*
22
23
  import org.json.JSONArray
23
24
  import org.json.JSONObject
25
+ import java.io.File
24
26
  import java.util.Properties
27
+ import java.util.ServiceLoader
25
28
  import java.util.concurrent.atomic.AtomicBoolean
26
29
 
27
30
  private data class FilePickerOptions(
@@ -151,6 +154,61 @@ class NativeBridge(
151
154
 
152
155
  private var networkMonitor: NetworkMonitor? = null
153
156
 
157
+ // AI plugin — loaded via reflection so NativeBridge.kt compiles with no compile-time dep on
158
+ // :catalyst-ai. When the module is absent the try block returns null gracefully.
159
+ private val aiBridge: Any? = try {
160
+ @Suppress("UNCHECKED_CAST")
161
+ run {
162
+ val bridgeClass = Class.forName("io.catalyst.nativeai.AIBridge")
163
+ val callbacksClass = Class.forName("io.catalyst.nativeai.AIBridgeCallbacks")
164
+ @Suppress("UNCHECKED_CAST")
165
+ val loader = ServiceLoader.load(bridgeClass as Class<Any>)
166
+ val bridge = loader.iterator().takeIf { it.hasNext() }?.next() ?: return@run null
167
+
168
+ val callbacksProxy = java.lang.reflect.Proxy.newProxyInstance(
169
+ bridgeClass.classLoader,
170
+ arrayOf(callbacksClass)
171
+ ) { _, method, args ->
172
+ val a = args ?: emptyArray()
173
+ when (method.name) {
174
+ "onReady" -> {
175
+ val payload = org.json.JSONObject().apply {
176
+ put("url", a[0]); put("port", a[1]); put("sessionId", a[2])
177
+ }
178
+ BridgeUtils.notifyWebJson(webView, BridgeUtils.WebEvents.ON_AI_READY, payload)
179
+ }
180
+ "onProgress" -> {
181
+ val payload = org.json.JSONObject().apply {
182
+ put("phase", a[0]); put("percent", a[1])
183
+ put("bytesLoaded", a[2]); put("bytesTotal", a[3]); put("detail", a[4])
184
+ }
185
+ BridgeUtils.notifyWebJson(webView, BridgeUtils.WebEvents.ON_AI_PROGRESS, payload)
186
+ }
187
+ "onLog" -> {
188
+ val payload = org.json.JSONObject().apply { put("message", a[0]) }
189
+ BridgeUtils.notifyWebJson(webView, BridgeUtils.WebEvents.ON_AI_LOG, payload)
190
+ }
191
+ "onError" -> BridgeUtils.notifyWebError(webView, BridgeUtils.WebEvents.ON_AI_ERROR, a[0] as String)
192
+ "isFrameworkServerRunning" -> FrameworkServerUtils.isRunning()
193
+ "getFrameworkServerPort" -> FrameworkServerUtils.getServerPort()
194
+ "getFrameworkServerSessionId" -> FrameworkServerUtils.getSessionId()
195
+ "setNativeAiSupplier" -> {
196
+ @Suppress("UNCHECKED_CAST")
197
+ FrameworkServerUtils.setNativeAiSupplier(
198
+ a[0] as suspend (String, org.json.JSONObject, String?) -> Pair<String, kotlinx.coroutines.flow.Flow<String>>
199
+ )
200
+ }
201
+ "setNativeSystemPrompt" -> FrameworkServerUtils.setNativeSystemPrompt(a[0] as String)
202
+ else -> null
203
+ }
204
+ }
205
+
206
+ bridge.javaClass.getMethod("attach", android.app.Activity::class.java, android.webkit.WebView::class.java, callbacksClass)
207
+ .invoke(bridge, mainActivity, webView, callbacksProxy)
208
+ bridge
209
+ }
210
+ } catch (_: Throwable) { null }
211
+
154
212
  // Video stream manager
155
213
  private var nativeCameraManager: NativeCameraManager? = null
156
214
  private var webViewOriginalBackground: Int = android.graphics.Color.WHITE
@@ -1301,6 +1359,34 @@ class NativeBridge(
1301
1359
  }
1302
1360
  }
1303
1361
 
1362
+ // -------------------------------------------------------------------------
1363
+ // Native AI bridge methods — delegated to AIBridge plugin (ServiceLoader)
1364
+ // -------------------------------------------------------------------------
1365
+
1366
+ @JavascriptInterface
1367
+ fun isAIAvailable(): Boolean = aiBridge != null
1368
+
1369
+ @JavascriptInterface
1370
+ fun initAI(optionsRaw: String?) {
1371
+ BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_AI_ERROR, "initialize AI") {
1372
+ aiBridge?.javaClass?.getMethod("initAI", String::class.java)?.invoke(aiBridge, optionsRaw)
1373
+ }
1374
+ }
1375
+
1376
+ @JavascriptInterface
1377
+ fun generateNative(optionsRaw: String?) {
1378
+ BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_AI_ERROR, "generate with native AI") {
1379
+ aiBridge?.javaClass?.getMethod("generateNative", String::class.java)?.invoke(aiBridge, optionsRaw)
1380
+ }
1381
+ }
1382
+
1383
+ @JavascriptInterface
1384
+ fun clearNativeConversation() {
1385
+ BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_AI_ERROR, "clear native conversation") {
1386
+ aiBridge?.javaClass?.getMethod("clearConversation")?.invoke(aiBridge)
1387
+ }
1388
+ }
1389
+
1304
1390
  /**
1305
1391
  * Cleanup method to be called when the bridge is being destroyed
1306
1392
  */
@@ -97,6 +97,14 @@ object BridgeUtils {
97
97
  ON_TRANSITION_CANCELLED("ON_TRANSITION_CANCELLED"),
98
98
  ON_TRANSITION_TIMEOUT("ON_TRANSITION_TIMEOUT"),
99
99
 
100
+ // Native AI events
101
+ ON_AI_READY("ON_AI_READY"),
102
+ ON_AI_ERROR("ON_AI_ERROR"),
103
+ ON_AI_TOKEN("ON_AI_TOKEN"),
104
+ ON_AI_DONE("ON_AI_DONE"),
105
+ ON_AI_PROGRESS("ON_AI_PROGRESS"),
106
+ ON_AI_LOG("ON_AI_LOG"),
107
+
100
108
  // Perf telemetry
101
109
  CATALYST_PERF_EVENT("CATALYST_PERF_EVENT"),
102
110
  }