catalyst-core 0.2.0-beta.3 → 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.
Files changed (160) 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/index.jsx +8 -0
  6. package/dist/logger.js +74 -3
  7. package/dist/native/androidSetup.js +300 -8
  8. package/dist/native/bridge/WebBridge.js +481 -59
  9. package/dist/native/bridge/constants/NativeInterfaces.js +290 -45
  10. package/dist/native/bridge/errors.js +250 -26
  11. package/dist/native/bridge/hooks/index.js +125 -1
  12. package/dist/native/bridge/hooks/useCamera.js +347 -9
  13. package/dist/native/bridge/hooks/useDataProtection.js +200 -2
  14. package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
  15. package/dist/native/bridge/hooks/useFilePicker.js +429 -2
  16. package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
  17. package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
  18. package/dist/native/bridge/hooks/useIntent.js +95 -1
  19. package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
  20. package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
  21. package/dist/native/bridge/hooks/useNotification.js +318 -2
  22. package/dist/native/bridge/hooks/useSafeArea.js +84 -2
  23. package/dist/native/bridge/hooks/useVideoStream.js +434 -6
  24. package/dist/native/bridge/hooks/utils.js +27 -1
  25. package/dist/native/bridge/hooks.js +16 -1
  26. package/dist/native/bridge/perf/collectors/ApiCollector.js +66 -3
  27. package/dist/native/bridge/perf/collectors/CacheCollector.js +186 -11
  28. package/dist/native/bridge/perf/collectors/HookCollector.js +141 -19
  29. package/dist/native/bridge/perf/collectors/InsightsCollector.js +481 -23
  30. package/dist/native/bridge/perf/collectors/InteractionCollector.js +245 -10
  31. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +107 -8
  32. package/dist/native/bridge/perf/collectors/NavigationCollector.js +273 -32
  33. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +178 -2
  34. package/dist/native/bridge/perf/collectors/RenderCollector.js +258 -14
  35. package/dist/native/bridge/perf/collectors/ScrollCollector.js +95 -9
  36. package/dist/native/bridge/perf/collectors/ThreadCollector.js +90 -6
  37. package/dist/native/bridge/perf/core/constants.js +103 -20
  38. package/dist/native/bridge/perf/core/measure.js +179 -30
  39. package/dist/native/bridge/perf/core/session.js +89 -23
  40. package/dist/native/bridge/perf/core/store.js +554 -1
  41. package/dist/native/bridge/perf/core/summary.js +174 -1
  42. package/dist/native/bridge/perf/core/traceExport.js +240 -1
  43. package/dist/native/bridge/perf/core/waterfalls.js +146 -1
  44. package/dist/native/bridge/perf/index.js +359 -38
  45. package/dist/native/bridge/safeArea.js +53 -5
  46. package/dist/native/bridge/standardizedHooks.js +595 -82
  47. package/dist/native/bridge/useBaseHook.js +274 -38
  48. package/dist/native/bridge/utils/CameraUtils.js +201 -56
  49. package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
  50. package/dist/native/bridge/utils/NativeBridge.js +425 -141
  51. package/dist/native/buildAndroid/assets.js +510 -13
  52. package/dist/native/buildAndroid/build.js +296 -19
  53. package/dist/native/buildAndroid/config.js +87 -1
  54. package/dist/native/buildAndroid/index.js +374 -1
  55. package/dist/native/buildAppAndroid.js +34 -2
  56. package/dist/native/buildAppIos.js +62 -2
  57. package/dist/native/buildIos/assets.js +609 -8
  58. package/dist/native/buildIos/build.js +639 -20
  59. package/dist/native/buildIos/config.js +294 -12
  60. package/dist/native/buildIos/index.js +343 -6
  61. package/dist/native/buildIos/plugins.js +343 -16
  62. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  63. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  64. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  65. package/dist/native/internalPluginUtils.js +232 -7
  66. package/dist/native/package.json +3 -0
  67. package/dist/native/plugin-bridge/PluginBridge.js +227 -1
  68. package/dist/native/pluginComposerAndroid.js +407 -23
  69. package/dist/native/pluginComposerIos.js +435 -11
  70. package/dist/native/renameAndroidProject.js +1094 -88
  71. package/dist/native/setupEmulatorIos.js +289 -22
  72. package/dist/native/setupServer.js +160 -19
  73. package/dist/native/terminalProgress.js +199 -11
  74. package/dist/native/utils.js +193 -15
  75. package/dist/offline/catalyst-sw.js +145 -1
  76. package/dist/offline/registerServiceWorker.js +33 -1
  77. package/dist/otel.js +375 -46
  78. package/dist/router/ClientRouter.js +14 -2
  79. package/dist/router/ServerRouter.js +9 -1
  80. package/dist/router.js +1 -1
  81. package/dist/scripts/build.js +112 -3
  82. package/dist/scripts/checkVersion.js +3 -1
  83. package/dist/scripts/cleanCache.js +30 -1
  84. package/dist/scripts/generateOfflineManifest.js +116 -1
  85. package/dist/scripts/loadEnvironmentVariables.js +32 -6
  86. package/dist/scripts/plugins.js +309 -1
  87. package/dist/scripts/preServerInit.js +13 -0
  88. package/dist/scripts/scriptUtils.js +54 -3
  89. package/dist/scripts/serve.js +51 -4
  90. package/dist/scripts/start.js +47 -10
  91. package/dist/scripts/validator.js +149 -1
  92. package/dist/sentry.cjs +127 -0
  93. package/dist/sentry.js +125 -5
  94. package/dist/server/expressServer.js +250 -5
  95. package/dist/server/manifestCache.js +36 -0
  96. package/dist/server/renderer/ChunkExtractor.js +144 -0
  97. package/dist/server/renderer/document/Body.jsx +59 -0
  98. package/dist/server/renderer/document/FastRefresh.jsx +20 -0
  99. package/dist/server/renderer/document/Head.jsx +60 -0
  100. package/dist/server/renderer/document/index.jsx +2 -0
  101. package/dist/server/renderer/extract.js +149 -31
  102. package/dist/server/renderer/handler.jsx +433 -0
  103. package/dist/server/renderer/index.js +8 -1
  104. package/dist/server/renderer/render.js +47 -0
  105. package/dist/server/utils/botDetectionMiddleware.js +29 -0
  106. package/dist/server/utils/cjsRequire.js +10 -0
  107. package/dist/server/utils/userAgentUtil.js +87 -9
  108. package/dist/server/utils/validator.js +164 -1
  109. package/dist/vite/FastRefresh.jsx +20 -0
  110. package/dist/vite/inject-cache-key-plugin.js +276 -0
  111. package/dist/vite/loadCustomViteConfig.js +22 -0
  112. package/dist/vite/manifest-categorization-plugin.js +455 -0
  113. package/dist/vite/node-loader.mjs +458 -0
  114. package/dist/vite/scssParams.js +21 -0
  115. package/dist/vite/vite.config.client.js +130 -0
  116. package/dist/vite/vite.config.js +422 -0
  117. package/dist/vite/vite.config.server.js +84 -0
  118. package/dist/web-router/components/MetaTag.jsx +34 -0
  119. package/dist/web-router/components/RouterDataProvider.jsx +332 -0
  120. package/dist/web-router/components/Split.jsx +200 -0
  121. package/dist/web-router/components/SplitInview.jsx +106 -0
  122. package/dist/web-router/components/SsrRequestContext.jsx +14 -0
  123. package/dist/web-router/context.jsx +12 -0
  124. package/dist/web-router/hooks.jsx +72 -0
  125. package/dist/web-router/types/components/MetaTag.d.ts +2 -0
  126. package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
  127. package/dist/web-router/types/context.d.ts +14 -0
  128. package/dist/web-router/types/hooks.d.ts +1 -0
  129. package/dist/web-router/types/index.d.ts +3 -0
  130. package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
  131. package/dist/web-router/utils/metaDataUtils.jsx +119 -0
  132. package/mcp_v2/conversion-tasks.json +35 -17
  133. package/mcp_v2/knowledge-base.json +124 -13
  134. package/mcp_v2/lib/helpers.js +10 -0
  135. package/mcp_v2/mcp.js +4 -4
  136. package/mcp_v2/package.json +1 -0
  137. package/mcp_v2/tools/build.js +116 -20
  138. package/mcp_v2/tools/conversion.js +106 -5
  139. package/mcp_v2/tools/tasks.js +18 -14
  140. package/package.json +35 -68
  141. package/babel.config.js +0 -32
  142. package/dist/index.js +0 -1
  143. package/dist/scripts/devBuild.js +0 -3
  144. package/dist/scripts/devServe.js +0 -3
  145. package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
  146. package/dist/scripts/registerAliases.js +0 -6
  147. package/dist/server/renderer/document/Body.js +0 -11
  148. package/dist/server/renderer/document/Head.js +0 -9
  149. package/dist/server/renderer/document/index.js +0 -1
  150. package/dist/server/renderer/handler.js +0 -47
  151. package/dist/server/startServer.js +0 -57
  152. package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
  153. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
  154. package/dist/webpack/babel.config.client.js +0 -1
  155. package/dist/webpack/babel.config.ssr.js +0 -1
  156. package/dist/webpack/base.babel.js +0 -7
  157. package/dist/webpack/development.client.babel.js +0 -10
  158. package/dist/webpack/production.client.babel.js +0 -4
  159. package/dist/webpack/production.ssr.babel.js +0 -8
  160. package/dist/webpack/scssParams.js +0 -9
@@ -1,7 +1,12 @@
1
1
  "use strict"
2
2
  const fs = require("fs")
3
3
  const path = require("path")
4
- const { makeProjectHelpers, versionOlderThan } = require("../lib/helpers")
4
+ const {
5
+ makeProjectHelpers,
6
+ versionOlderThan,
7
+ catalystGeneration,
8
+ VITE_RUNTIME_VERSION,
9
+ } = require("../lib/helpers")
5
10
 
6
11
  // Tasks that require a minimum catalyst-core version.
7
12
  // Key = task ID, value = { minVersion, reason, upgradeNote }
@@ -35,6 +40,7 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
35
40
 
36
41
  // ── Version gate: read installed catalyst-core version ────────────────────
37
42
  const installedVersion = _projectInfo.installedVersion || null
43
+ const generation = catalystGeneration(installedVersion)
38
44
 
39
45
  // ── Load project state once ────────────────────────────────────────────────
40
46
  const config = readJson("config/config.json")
@@ -78,14 +84,36 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
78
84
  },
79
85
 
80
86
  T2_ROUTER_DEP: () => {
81
- if (!allDeps["@tata1mg/router"])
87
+ if (generation === "unknown")
88
+ return {
89
+ status: "needs_review",
90
+ native_risk: "Router ownership differs between Catalyst 0.2.x and 0.3.x.",
91
+ reason: `Unable to determine the installed catalyst-core version. Confirm whether the target is legacy 0.2.x or current ${VITE_RUNTIME_VERSION}+ before changing router dependencies.`,
92
+ review_context: {
93
+ question: "Which catalyst-core version is this project targeting?",
94
+ what_correct_looks_like:
95
+ "0.2.x uses @tata1mg/router; 0.3.x+ removes it and imports Catalyst router APIs from catalyst-core.",
96
+ what_gap_looks_like:
97
+ "Router dependencies and imports are mixed across runtime generations.",
98
+ },
99
+ }
100
+ if (generation === "legacy") {
101
+ if (!allDeps["@tata1mg/router"])
102
+ return {
103
+ status: "gap",
104
+ native_risk: "Catalyst 0.2.x requires its external router package for SSR routing.",
105
+ reason: "Legacy Catalyst detected but @tata1mg/router is missing",
106
+ }
107
+ return { status: "completed", note: "Legacy 0.2.x external router dependency detected." }
108
+ }
109
+ if (allDeps["@tata1mg/router"])
82
110
  return {
83
111
  status: "gap",
84
112
  native_risk:
85
- "react-router-dom does not support SSR + hydration required by catalyst-core. App routing will break on native.",
86
- reason: "@tata1mg/router not in package.json dependencies",
113
+ "Catalyst 0.3.x owns the router implementation. Keeping @tata1mg/router can install a second routing runtime and produce incompatible React elements.",
114
+ reason: "Remove @tata1mg/router and import router APIs from catalyst-core",
87
115
  }
88
- return { status: "completed" }
116
+ return { status: "completed", note: "Current 0.3.x integrated router contract detected." }
89
117
  },
90
118
 
91
119
  T3_ROUTES_FILE: () => {
@@ -111,6 +139,38 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
111
139
  return { status: "completed" }
112
140
  },
113
141
 
142
+ T2b_RUNTIME_DEPS: () => {
143
+ if (generation !== "current")
144
+ return {
145
+ status: generation === "legacy" ? "completed" : "needs_review",
146
+ reason:
147
+ generation === "legacy"
148
+ ? "Legacy 0.2.x dependency contract retained."
149
+ : "Catalyst version is unknown; do not change React versions until the target is confirmed.",
150
+ native_risk:
151
+ generation === "unknown"
152
+ ? "Applying React 19 requirements to an unknown Catalyst runtime can break SSR."
153
+ : undefined,
154
+ review_context:
155
+ generation === "unknown"
156
+ ? { question: "Is this project staying on 0.2.x or targeting 0.3.x?" }
157
+ : undefined,
158
+ }
159
+ const invalid = []
160
+ if (allDeps.react !== "19.0.0") invalid.push(`react=${allDeps.react || "missing"}`)
161
+ if (allDeps["react-dom"] !== "19.0.0")
162
+ invalid.push(`react-dom=${allDeps["react-dom"] || "missing"}`)
163
+ if (allDeps["@loadable/component"]) invalid.push("@loadable/component is installed")
164
+ if (invalid.length)
165
+ return {
166
+ status: "gap",
167
+ native_risk:
168
+ "Mixed React or legacy loadable runtimes can cause invalid React child, hook, or hydration failures.",
169
+ reason: invalid.join(", "),
170
+ }
171
+ return { status: "completed" }
172
+ },
173
+
114
174
  T4_DATA_FETCHING: () => {
115
175
  const useEffectFiles = grepSrc(
116
176
  /useEffect.*fetch|useEffect.*axios|useEffect.*\.get\(|useEffect.*\.post\(/
@@ -198,6 +258,40 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
198
258
  "Client entry files are required for hydration. Without them the native WebView loads server HTML but JS never hydrates — app is non-interactive.",
199
259
  reason: `Missing: ${missing.join(", ")}`,
200
260
  }
261
+ const clientEntry = readText("client/index.js") || ""
262
+ if (generation === "current" && /@loadable\/component|loadableReady/.test(clientEntry))
263
+ return {
264
+ status: "gap",
265
+ native_risk:
266
+ "Catalyst 0.3.x uses its Vite split hydration contract; loadableReady belongs to the legacy webpack runtime.",
267
+ reason: "Replace loadableReady with hydrationReady from catalyst-core",
268
+ }
269
+ return { status: "completed" }
270
+ },
271
+
272
+ T7b_NATIVE_SCRIPTS: () => {
273
+ const scripts = pkg.scripts || {}
274
+ const required = [
275
+ "buildApp",
276
+ "buildApp:ios",
277
+ "buildApp:android",
278
+ "setupEmulator",
279
+ "setupEmulator:ios",
280
+ "setupEmulator:android",
281
+ ]
282
+ const missing = required.filter((name) => !scripts[name])
283
+ if (missing.length)
284
+ return {
285
+ status: "gap",
286
+ native_risk: `Native Catalyst commands are unavailable: ${missing.join(", ")}.`,
287
+ reason: `Missing package scripts: ${missing.join(", ")}`,
288
+ }
289
+ if (generation === "current" && (scripts.devBuild || scripts.devServe))
290
+ return {
291
+ status: "gap",
292
+ native_risk: "devBuild and devServe were removed from the Catalyst 0.3.x CLI.",
293
+ reason: "Remove devBuild/devServe and use start for development or build + serve for production",
294
+ }
201
295
  return { status: "completed" }
202
296
  },
203
297
 
@@ -657,6 +751,13 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
657
751
  project: pkg.name || root,
658
752
  scanned_at: new Date().toISOString(),
659
753
  catalyst_core_version: installedVersion || "unknown",
754
+ catalyst_generation: generation,
755
+ guidance:
756
+ generation === "unknown"
757
+ ? `Specify a target Catalyst version before applying router, build, or hydration changes. Current Vite guidance starts at ${VITE_RUNTIME_VERSION}.`
758
+ : generation === "legacy"
759
+ ? "Using legacy Catalyst 0.2.x webpack/router guidance. Request an upgrade plan explicitly to target 0.3.x."
760
+ : "Using current Catalyst 0.3.x Vite/integrated-router guidance.",
660
761
  project_context,
661
762
  ...(versionBanner ? { version_warning: versionBanner } : {}),
662
763
  summary: {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require("fs")
4
4
  const path = require("path")
5
- const { findCatalystRoot } = require("../lib/helpers")
5
+ const { findCatalystRoot, catalystGeneration } = require("../lib/helpers")
6
6
  const conversion = require("./conversion")
7
7
 
8
8
  let _db
@@ -597,33 +597,34 @@ function appendToMdFindings(projectRoot, slug, finding) {
597
597
 
598
598
  // Commands that are verified to exist in catalyst-core projects
599
599
  const VALID_COMMANDS = new Set([
600
- "npm run build:android",
601
- "npm run build:android:release",
602
- "npm run build:ios",
600
+ "npm run start",
601
+ "npm run build",
602
+ "npm run serve",
603
603
  "npm run buildApp:android",
604
604
  "npm run buildApp:ios",
605
- "npm run buildApp:android:release",
606
- "npm run buildApp:ios:release",
607
605
  "npm run setupEmulator:android",
608
606
  "npm run setupEmulator:ios",
609
- "npm run devBuild",
610
- "npm run devServe",
611
607
  "npm run prepare",
612
608
  "node .catalyst/mcp/setup.js",
613
609
  ])
614
610
 
615
- function validateStepCommands(steps) {
611
+ function validateStepCommands(steps, generation) {
612
+ const validCommands = new Set(VALID_COMMANDS)
613
+ if (generation === "legacy") {
614
+ validCommands.add("npm run devBuild")
615
+ validCommands.add("npm run devServe")
616
+ }
616
617
  const invalid = []
617
618
  for (const s of steps) {
618
619
  const text = `${typeof s === "string" ? s : (s.title || "") + " " + (s.detail || "")}`
619
620
  const cmds = text.match(/npm [\w:]+(?:\s[\w:]+)?|npx [\w@/-]+|node [\w./]+/g) || []
620
621
  for (const cmd of cmds) {
621
- if (!VALID_COMMANDS.has(cmd)) {
622
+ if (!validCommands.has(cmd)) {
622
623
  invalid.push(cmd)
623
624
  }
624
625
  }
625
626
  }
626
- return invalid
627
+ return { invalid, validCommands }
627
628
  }
628
629
 
629
630
  function handle_create_task_plan({ goal, steps: customSteps } = {}) {
@@ -650,13 +651,16 @@ function handle_create_task_plan({ goal, steps: customSteps } = {}) {
650
651
 
651
652
  if (customSteps && Array.isArray(customSteps) && customSteps.length) {
652
653
  // Validate any commands in custom steps against known catalyst commands
653
- const invalidCmds = validateStepCommands(customSteps)
654
+ const { invalid: invalidCmds, validCommands } = validateStepCommands(
655
+ customSteps,
656
+ catalystGeneration(catalystRoot.installedVersion || catalystRoot.catalystVersion)
657
+ )
654
658
  if (invalidCmds.length > 0) {
655
659
  return {
656
660
  error: "invalid_commands_in_steps",
657
- message: `Steps contain commands that do not exist in catalyst-core projects: ${invalidCmds.join(", ")}. Do not invent commands. Valid catalyst commands are: ${[...VALID_COMMANDS].join(", ")}. For conversion/migration goals, omit steps entirely — auto-generation runs live file detection and builds accurate steps.`,
661
+ message: `Steps contain commands that do not exist for this catalyst-core generation: ${invalidCmds.join(", ")}. Do not invent commands. Valid commands are: ${[...validCommands].join(", ")}. For conversion/migration goals, omit steps entirely so live detection can generate version-aware steps.`,
658
662
  invalid_commands: invalidCmds,
659
- valid_commands: [...VALID_COMMANDS],
663
+ valid_commands: [...validCommands],
660
664
  }
661
665
  }
662
666
  steps = customSteps.map((s, i) => ({
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.2.0-beta.3",
4
- "main": "dist/index.js",
3
+ "version": "0.3.0-beta.1",
4
+ "type": "module",
5
+ "main": "./dist/index.jsx",
5
6
  "description": "Web framework that provides great performance out of the box",
6
7
  "bin": {
7
8
  "catalyst": "bin/catalyst.js"
@@ -12,97 +13,59 @@
12
13
  "directory": "packages/catalyst-core"
13
14
  },
14
15
  "_moduleAliases": {
15
- "@catalyst/template": ".",
16
- "@catalyst/template/src": "src",
17
- "@catalyst/template/routes": "src/js/routes",
18
- "@catalyst/template/server": "server",
19
- "@catalyst/template/store": "src/js/store",
20
- "@catalyst/template/config": "config",
21
- "@catalyst": ".",
22
- "@catalyst/root": "../",
23
- "@catalyst/router": "router",
24
- "@catalyst/scripts": "scripts",
25
- "@catalyst/server": "server",
26
- "@catalyst/webpack": "webpack"
16
+ "@catalyst/template": "."
27
17
  },
28
18
  "exports": {
29
- ".": "./dist/index.js",
19
+ ".": "./dist/index.jsx",
30
20
  "./package.json": "./package.json",
31
21
  "./logger": "./dist/logger.js",
32
22
  "./caching": "./dist/caching.js",
33
23
  "./router/ClientRouter": "./dist/router/ClientRouter.js",
24
+ "./document": "./dist/server/renderer/document/index.jsx",
34
25
  "./WebBridge": "./dist/native/bridge/WebBridge.js",
35
26
  "./hooks": "./dist/native/bridge/hooks.js",
36
- "./sentry": "./dist/sentry.js",
27
+ "./sentry": "./dist/sentry.cjs",
37
28
  "./otel": "./dist/otel.js"
38
29
  },
39
30
  "scripts": {
40
31
  "lint": "eslint .",
32
+ "lint-staged": "lint-staged",
41
33
  "prettify": "prettier . --write",
42
- "prepare": "babel src --out-dir ./dist --ignore '**/.build/**' && mkdir -p dist/native && cp -r src/native/androidProject src/native/build.swift src/native/assets dist/native/ && mkdir -p dist/native/iosnativeWebView && tar -C src/native/iosnativeWebView --exclude='.build' --exclude='.package-config-hash' -cf - . | tar -C dist/native/iosnativeWebView -xf -",
34
+ "prepare": "rm -rf ./dist && cp -r ./src ./dist && babel src/native --out-dir dist/native --config-file ./babel.native.config.cjs --ignore '**/.build/**' && babel src/sentry.js --out-file dist/sentry.cjs --config-file ./babel.native.config.cjs",
43
35
  "prepublishOnly": "npm run prepare",
44
36
  "test:fixture": "sh ../../scripts/test-catalyst-core.sh"
45
37
  },
46
38
  "license": "MIT",
47
39
  "dependencies": {
48
- "@babel/cli": "^7.23.0",
49
- "@babel/core": "^7.23.2",
50
- "@babel/node": "^7.22.19",
51
- "@babel/preset-env": "^7.23.2",
52
- "@babel/preset-react": "^7.22.15",
53
- "@babel/preset-typescript": "^7.24.7",
54
- "@babel/register": "^7.22.15",
55
- "@dr.pogodin/css-modules-require-hook": "^4.7.3",
56
- "@loadable/babel-plugin": "^5.16.1",
57
- "@loadable/component": "^5.15.3",
58
- "@loadable/server": "^5.16.1",
59
- "@loadable/webpack-plugin": "^5.15.2",
60
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
61
- "@svgr/webpack": "^8.1.0",
62
- "@tata1mg/router": "0.1.0-beta",
63
- "app-root-path": "^3.1.0",
64
- "babel-loader": "^9.1.3",
65
- "babel-plugin-react-compiler": "^19.0.0-beta-decd7b8-20250118",
40
+ "@vitejs/plugin-react": "^4.4.1",
66
41
  "body-parser": "^1.20.2",
67
- "chokidar": "^3.6.0",
68
42
  "compression": "^1.7.4",
69
43
  "cookie-parser": "^1.4.6",
70
- "cross-env": "^7.0.3",
71
- "css-hot-loader": "^1.4.4",
72
- "css-loader": "^6.8.1",
73
44
  "express": "^4.18.2",
74
45
  "express-static-gzip": "^2.1.7",
75
- "file-loader": "^6.2.0",
76
- "html-loader": "^4.2.0",
77
- "img-loader": "^4.0.0",
78
- "mini-css-extract-plugin": "^2.7.6",
79
- "module-alias": "^2.2.3",
80
46
  "picocolors": "^1.0.0",
81
- "postcss-loader": "^7.3.3",
82
- "postcss-scss": "^4.0.9",
83
47
  "react": "19.0.0",
84
- "react-compiler-runtime": "^19.0.0-beta-decd7b8-20250118",
85
48
  "react-dom": "19.0.0",
49
+ "react-helmet-async": "^1.3.0",
86
50
  "react-redux": "^9.2.0",
87
51
  "react-refresh": "^0.14.0",
88
52
  "react-router-dom": "^6.16.0",
89
- "sass": "^1.69.5",
90
- "sass-loader": "^13.3.2",
91
- "sass-resources-loader": "^2.2.5",
53
+ "redux": "^4.2.1",
54
+ "sanitize-html": "^2.13.1",
55
+ "sass": "^1.89.2",
92
56
  "ua-parser-js": "^1.0.37",
93
- "url": "^0.11.3",
94
- "url-loader": "^4.1.1",
95
- "webpack": "^5.88.2",
96
- "webpack-bundle-analyzer": "^4.9.1",
97
- "webpack-cli": "^5.1.4",
98
- "webpack-dev-server": "^5.2.4",
99
- "webpack-merge": "^5.9.0",
100
- "webpack-node-externals": "^3.0.0",
57
+ "vite": "^6.4.3",
58
+ "vite-plugin-svgr": "^5.0.0",
101
59
  "winston": "^3.11.0",
102
60
  "winston-daily-rotate-file": "^5.0.0"
103
61
  },
104
62
  "devDependencies": {
63
+ "@babel/cli": "^7.23.0",
64
+ "@babel/core": "^7.23.2",
105
65
  "@babel/eslint-parser": "^7.19.1",
66
+ "@babel/preset-env": "^7.23.2",
67
+ "@babel/preset-react": "^7.22.15",
68
+ "@babel/preset-typescript": "^7.24.7",
106
69
  "@commitlint/cli": "^19.3.0",
107
70
  "@commitlint/config-conventional": "^19.2.2",
108
71
  "eslint": "^8.26.0",
@@ -112,20 +75,24 @@
112
75
  "eslint-plugin-react-hooks": "^4.6.0",
113
76
  "eslint-plugin-risxss": "^2.1.0",
114
77
  "eslint-plugin-security": "^3.0.0",
78
+ "husky": "^9.0.11",
79
+ "lint-staged": "^15.2.2",
115
80
  "prettier": "^3.2.5"
116
81
  },
82
+ "lint-staged": {
83
+ "*.{js,jsx}": [
84
+ "eslint .",
85
+ "prettier . --write"
86
+ ]
87
+ },
117
88
  "overrides": {
118
- "@loadable/component": {
119
- "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || 19.0.0"
120
- },
121
- "@loadable/server": {
122
- "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || 19.0.0"
89
+ "react-helmet-async": {
90
+ "react": "19.0.0",
91
+ "react-dom": "19.0.0"
123
92
  },
124
- "uuid": "11.1.1",
125
- "file-type": "22.0.1",
126
- "imagemin": "9.0.1",
127
- "webpack-dev-server": {
128
- "http-proxy-middleware": "3.0.7"
93
+ "react-redux": {
94
+ "react": "19.0.0",
95
+ "react-dom": "19.0.0"
129
96
  }
130
97
  }
131
98
  }
package/babel.config.js DELETED
@@ -1,32 +0,0 @@
1
- module.exports = (api) => {
2
- api.cache(true)
3
-
4
- return {
5
- presets: [
6
- [
7
- "@babel/preset-typescript",
8
- {
9
- isTSX: true,
10
- allExtensions: true,
11
- },
12
- ],
13
- [
14
- "@babel/preset-env",
15
- {
16
- targets: {
17
- node: "current",
18
- },
19
- },
20
- ],
21
- ["@babel/preset-react", { runtime: "automatic" }],
22
- ],
23
- compact: true,
24
- plugins: ["@loadable/babel-plugin"],
25
- env: {
26
- test: {
27
- presets: ["@babel/preset-react"],
28
- },
29
- },
30
- ignore: ["__TEST__"],
31
- }
32
- }
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});var _index=require("./server/renderer/document/index");Object.keys(_index).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_index[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _index[key];}});});
@@ -1,3 +0,0 @@
1
- "use strict";const path=require("path");const{green,cyan,yellow}=require("picocolors");const{name}=require(`${process.cwd()}/package.json`);const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);const{arrayToObject,printBundleInformation,runBuildCommands}=require("./scriptUtils.js");/**
2
- * @description - creates a production build of the application.
3
- */function devBuild(){const isWindows=process.platform==="win32";const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");const commands=["node ./dist/scripts/checkVersion",`${isWindows?"rd -r -fo":"rm -rf"} ${process.cwd()}/${BUILD_OUTPUT_PATH} & node ./dist/scripts/loadScriptsBeforeServerStarts.js`,`cross-env APPLICATION=${name||"catalyst_app"} webpack --config ./dist/webpack/production.client.babel.js --progress`,`cross-env APPLICATION=${name||"catalyst_app"} SSR=true webpack --config ./dist/webpack/production.ssr.babel.js`,`cross-env APPLICATION=${name||"catalyst_app"} node ./dist/scripts/generateOfflineManifest.js`,`cross-env APPLICATION=${name||"catalyst_app"} npx babel ./dist/server --out-dir ${process.cwd()}/${BUILD_OUTPUT_PATH} --ignore '**/*.test.js,./dist/server/renderer/handler.js' --quiet`,`cross-env APPLICATION=${name||"catalyst_app"} npx babel ${process.cwd()}/server --out-dir ${process.cwd()}/${BUILD_OUTPUT_PATH} --quiet`];console.log("Creating an optimized local build...");runBuildCommands({commands,cwd:dirname,env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:true,...argumentsObject}});console.log(green("Compiled successfully."));console.log("\nFile sizes after gzip:\n");printBundleInformation();console.log(`\nThe ${cyan(BUILD_OUTPUT_PATH)} folder is ready to be deployed.`);console.log("You may serve it with a serve command:");console.log(cyan("\n npm run devServe"));console.log("\nFind out more about deployment here:");console.log(yellow("\n https://catalyst.1mg.com/public_docs/content/Deployment%20and%20Production/deployment\n"));}devBuild();
@@ -1,3 +0,0 @@
1
- "use strict";const path=require("path");const{spawnSync}=require("child_process");const{arrayToObject}=require("./scriptUtils");const{name}=require(`${process.cwd()}/package.json`);const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);/**
2
- * @description - Serves production build of the application.
3
- */function devServe(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");spawnSync("node",["-r","./dist/scripts/loadScriptsBeforeServerStarts.js",path.join(process.cwd(),BUILD_OUTPUT_PATH,"startServer.js")],{cwd:dirname,stdio:"inherit",env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:true,APPLICATION:name||"catalyst_app",...argumentsObject}});}devServe();
@@ -1,4 +0,0 @@
1
- "use strict";require("./registerAliases.js");var _cssModulesRequireHook=_interopRequireDefault(require("@dr.pogodin/css-modules-require-hook"));var _path=_interopRequireDefault(require("path"));var _loadEnvironmentVariables=_interopRequireDefault(require("./loadEnvironmentVariables"));var _postcssScss=_interopRequireDefault(require("postcss-scss"));var _logger=require("@catalyst/logger.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable */const{cssModulesIdentifierDev}=require("@catalyst/root/config.json");// stores your config keys in enviorments variables
2
- (0,_loadEnvironmentVariables.default)();// creates logger instance with provided config and stores them at global level.
3
- (0,_logger.configureLogger)({enableDebugLogs:process.env.ENABLE_DEBUG_LOGS,enableFileLogging:process.env.ENABLE_FILE_LOGGING,enableConsoleLogging:process.env.ENABLE_CONSOLE_LOGGING});// compile css-variables in realtime in dev mode.
4
- if(process.env.NODE_ENV==="development")(0,_cssModulesRequireHook.default)({extensions:[".scss",".css"],processorOpts:{parser:_postcssScss.default.parse},generateScopedName:cssModulesIdentifierDev,devMode:true,ignore:_path.default.posix.join(process.env.src_path,"/src/static/css/base/(?!.*.scss$).*")});
@@ -1,6 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.catalystResultMap=void 0;var _path=_interopRequireDefault(require("path"));var _moduleAlias=_interopRequireDefault(require("module-alias"));var _package=require("../../package.json");var _validator=require("./validator.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const{_moduleAliases:templateModuleAliases}=require(`${process.env.src_path}/package.json`);const catalystResultMap=exports.catalystResultMap=Object.keys(_package._moduleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=_package._moduleAliases[aliasName];if(aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{// nosemgrep
2
- resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}return resultMap;}if(aliasName?.includes("@catalyst")&&!aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{// nosemgrep
3
- resultMap[aliasName]=_path.default.join(__dirname,"../",aliasPath);}return resultMap;}return resultMap;},{});_moduleAlias.default.addAliases(catalystResultMap);// resolves module alias imports
4
- if((0,_validator.validateModuleAlias)(templateModuleAliases)){_moduleAlias.default.addAliases(Object.keys(templateModuleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=templateModuleAliases[aliasName];if(aliasPath.includes("server")){if(process.env.NODE_ENV!=="production"){// nosemgrep
5
- resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}else{resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}}else{// nosemgrep
6
- resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}return resultMap;},{}));}
@@ -1,11 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.Body=Body;var _react=_interopRequireDefault(require("react"));var _jsxRuntime=require("react/jsx-runtime");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/**
2
- * Body component which will be used in page component
3
- * @param {object} jsx - page jsx code
4
- * @param {object} initialState - initial state object for redux store
5
- * @param {object} firstFoldCss - style elements extracted for initial page load
6
- * @param {object} fetcherData - contains data from executing serverFetcher function
7
- * @param {object} children - contains any child elements defined within the component
8
- */const DEFAULT_SAFE_AREA={top:0,right:0,bottom:0,left:0};function Body({jsx="",initialState={},fetcherData={},children,safeArea=DEFAULT_SAFE_AREA,nativeWebView=false}){return/*#__PURE__*/(0,_jsxRuntime.jsxs)("body",{children:[/*#__PURE__*/(0,_jsxRuntime.jsx)("script",{/* eslint-disable-next-line risxss/catch-potential-xss-react */dangerouslySetInnerHTML:{__html:`window.__SAFE_AREA_INITIAL__ = ${JSON.stringify(safeArea)}; window.__CATALYST_NATIVE_WEBVIEW__ = ${nativeWebView?"true":"false"}`}}),jsx,/*#__PURE__*/(0,_jsxRuntime.jsx)("script",{/* eslint-disable */dangerouslySetInnerHTML:{__html:`
9
- window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}
10
- window.__ROUTER_INITIAL_DATA__ = ${JSON.stringify(fetcherData)}
11
- `}}),children]});}
@@ -1,9 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.Head=Head;var _react=_interopRequireDefault(require("react"));var _jsxRuntime=require("react/jsx-runtime");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/**
2
- * Head component which will be used in page rendering
3
- * @param {boolean} isBot - checks if request is made by bot
4
- * @param {string} pageCss - includes all styles for page css
5
- * @param {object} preloadJSLinks - async scripts for loading chunks
6
- * @param {array} metaTags - user defined function which returns meta tags in array
7
- * @param {string} publicAssetPath - public asset path for assets
8
- * @param {object} children - contains any child elements defined within the component
9
- */function Head({pageCss,preloadJSLinks,metaTags,isBot,publicAssetPath,children}){if(children){return/*#__PURE__*/(0,_jsxRuntime.jsxs)("head",{children:[children,metaTags&&metaTags,pageCss&&/*#__PURE__*/(0,_jsxRuntime.jsx)("style",{dangerouslySetInnerHTML:{__html:pageCss}}),!isBot&&preloadJSLinks]});}return/*#__PURE__*/(0,_jsxRuntime.jsxs)("head",{children:[/*#__PURE__*/(0,_jsxRuntime.jsx)("meta",{charSet:"utf-8"}),/*#__PURE__*/(0,_jsxRuntime.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1"}),publicAssetPath&&/*#__PURE__*/(0,_jsxRuntime.jsx)("link",{rel:"preconnect",href:publicAssetPath}),publicAssetPath&&/*#__PURE__*/(0,_jsxRuntime.jsx)("link",{rel:"dns-prefetch",href:publicAssetPath}),metaTags&&metaTags,pageCss&&/*#__PURE__*/(0,_jsxRuntime.jsx)("style",{dangerouslySetInnerHTML:{__html:pageCss}}),!isBot&&preloadJSLinks]});}
@@ -1 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});var _Head=require("./Head");Object.keys(_Head).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_Head[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _Head[key];}});});var _Body=require("./Body");Object.keys(_Body).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_Body[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _Body[key];}});});
@@ -1,47 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _react=_interopRequireDefault(require("react"));var _extract=_interopRequireWildcard(require("./extract"));var _otel=require("../../otel");var _reactRedux=require("react-redux");var _document=require("./document");var _server=require("react-router-dom/server");var _ServerRouter=_interopRequireDefault(require("@catalyst/router/ServerRouter.js"));var _index=_interopRequireDefault(require("@catalyst/template/src/js/containers/App/index.js"));var _server2=require("@loadable/server");var _server3=require("react-dom/server");var _userAgentUtil=require("@catalyst/server/utils/userAgentUtil");var _router=require("@tata1mg/router");var _validator=require("@catalyst/server/utils/validator");var _document2=_interopRequireDefault(require("@catalyst/template/server/document.js"));var _utils=require("@catalyst/template/src/js/routes/utils.js");var _index2=require("@catalyst/template/server/index.js");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(i.get||i.set)?o(f,t,i):f[t]=e[t]);return f;})(e,t);}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const storePath=_path.default.resolve(`${process.env.src_path}/src/js/store/index.js`);let createStore;if(_fs.default.existsSync(storePath)){try{const{default:configureStore}=require(`${process.env.src_path}/src/js/store/index.js`);createStore=configureStore;}catch(error){createStore=()=>{return{getState:()=>{}};};}}else{createStore=()=>{return{getState:()=>{}};};}const isProduction=process.env.NODE_ENV==="production";const DEFAULT_SAFE_AREA_INSETS={top:0,right:0,bottom:0,left:0};const parseSafeAreaFromHeaders=req=>{const readEdge=header=>{const raw=req.get(header)??req.headers[header.toLowerCase()];const value=Number(raw);return Number.isFinite(value)&&value>=0?value:null;};const top=readEdge("X-Safe-Area-Top");const right=readEdge("X-Safe-Area-Right");const bottom=readEdge("X-Safe-Area-Bottom");const left=readEdge("X-Safe-Area-Left");const hasAny=[top,right,bottom,left].some(value=>value!==null);if(!hasAny){return null;}return{top:top??0,right:right??0,bottom:bottom??0,left:left??0};};const isNativeWebViewRequest=req=>{const raw=req.get("X-Catalyst-Native-WebView")??req.headers["x-catalyst-native-webview"];return raw==="1"||raw==="true";};// Cache webStats path - computed once at module load
2
- const webStatsPath=isProduction?_path.default.join(process.env.src_path,`${process.env.BUILD_OUTPUT_PATH}/public/loadable-stats.json`):_path.default.join(__dirname,"../../..",`loadable-stats.json`);// Cache for parsed stats (production only)
3
- let cachedWebStats=null;const getWebStats=()=>{if(!isProduction){// In development, stats can change; always read fresh.
4
- return JSON.parse(_fs.default.readFileSync(webStatsPath,"utf-8"));}if(!cachedWebStats){cachedWebStats=JSON.parse(_fs.default.readFileSync(webStatsPath,"utf-8"));logger.info("ChunkExtractor stats cached in memory");}return cachedWebStats;};// Cache for ChunkExtractor instances per route (production only)
5
- if(!process.extractorCache){process.extractorCache={};}const getOrCreateExtractorForRoute=routePath=>{if(!isProduction){// In dev, always create fresh extractor (HMR compatible)
6
- return new _server2.ChunkExtractor({stats:getWebStats(),entrypoints:["app"]});}// Production: cache extractor per route
7
- if(!process.extractorCache[routePath]){process.extractorCache[routePath]=new _server2.ChunkExtractor({stats:getWebStats(),entrypoints:["app"]});logger.info(`ChunkExtractor created and cached for route: ${routePath}`);}return process.extractorCache[routePath];};// Cache routes array - getRoutes() returns same reference, but validate once
8
- let cachedRoutes=null;const getCachedRoutes=()=>{if(!cachedRoutes){cachedRoutes=(0,_validator.validateGetRoutes)(_utils.getRoutes)?(0,_utils.getRoutes)():[];}return cachedRoutes;};const SSR_SERVICE=process.env.SERVICE_NAME||`pwa-${process.env.APPLICATION}-node-server-otel`;const traceHandlerHook=(fn,spanName)=>typeof fn==="function"?(0,_otel.withSyncObservability)(SSR_SERVICE,fn,spanName):fn;const onRouteMatch=traceHandlerHook(_index2.onRouteMatch,"onRouteMatch");const onFetcherSuccess=traceHandlerHook(_index2.onFetcherSuccess,"onFetcherSuccess");const onFetcherError=traceHandlerHook(_index2.onFetcherError,"onFetcherError");const onAppServerSideSuccess=traceHandlerHook(_index2.onAppServerSideSuccess,"onAppServerSideSuccess");const onAppServerSideError=traceHandlerHook(_index2.onAppServerSideError,"onAppServerSideError");const onRenderError=traceHandlerHook(_index2.onRenderError,"onRenderError");const onRequestError=traceHandlerHook(_index2.onRequestError,"onRequestError");// Phase 1b dry-run: wrapped separately so it appears as a distinct span inside getMatchRoutes.
9
- const renderToStringWithObservability=(0,_otel.withSyncObservability)(SSR_SERVICE,function dryRunRender(webExtractor,store,context,req,fetcherData){(0,_server3.renderToString)(/*#__PURE__*/(0,_jsxRuntime.jsx)(_server2.ChunkExtractorManager,{extractor:webExtractor,children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_reactRedux.Provider,{store:store,children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_server.StaticRouter,{context:context,location:req.originalUrl,children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_ServerRouter.default,{store:store,intialData:fetcherData})})})}));},"renderToString");// Internal recursive implementation — called directly to avoid creating a new span on each recursion.
10
- const _getMatchRoutes=(routes,req,res,store,context,fetcherData,basePath="")=>{return routes.reduce((matches,route)=>{const{path}=route;const match=(0,_router.matchPath)({path:`${basePath}/${path}`,caseSensitive:false,end:true},req.baseUrl||"/");if(match){if(!res.locals.pageCss&&!res.locals.preloadJSLinks&&!res.locals.routePath){res.locals.routePath=path;// Phase 1a: try to serve CSS/preloadLinks from the extractor cache (production only).
11
- // On a cache hit, res.locals.pageCss and preloadJSLinks are populated and
12
- // the renderToString dry-run below is skipped entirely for this request.
13
- (0,_extract.default)(res,route);}// Production: reuses the same ChunkExtractor per route (chunks accumulate over time).
14
- // Dev: always creates a fresh extractor so HMR chunk changes are picked up immediately.
15
- if(!res.locals.webExtractor){res.locals.webExtractor=getOrCreateExtractorForRoute(path);}if(!res.locals.pageCss&&!res.locals.preloadJSLinks){// Phase 1b: dry-run renderToString solely to let the ChunkExtractor record which
16
- // chunks this route needs. The output is discarded — the real render happens later
17
- // in renderMarkUp via renderToPipeableStream.
18
- renderToStringWithObservability(res.locals.webExtractor,store,context,req,fetcherData);}const wc=route.component;matches.push({route,match,serverSideFunction:wc&&wc.serverSideFunction||(()=>Promise.resolve())});}if(!match&&route.children){const nested=_getMatchRoutes(route.children,req,res,store,context,fetcherData,`${basePath}/${path}`);if(nested.length){matches=matches.concat(nested);}}return matches;},[]);};const getMatchRoutes=(0,_otel.withSyncObservability)(SSR_SERVICE,_getMatchRoutes,"getMatchRoutes");// Preloads chunks required for rendering document
19
- const getComponent=(store,context,req,fetcherData)=>{return/*#__PURE__*/(0,_jsxRuntime.jsx)("div",{id:"app",children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_reactRedux.Provider,{store:store,children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_server.StaticRouter,{context:context,location:req.originalUrl,children:/*#__PURE__*/(0,_jsxRuntime.jsx)(_ServerRouter.default,{store:store,intialData:fetcherData})})})});};const tracedResWriteFirstFoldCss=(0,_otel.withSyncObservability)(SSR_SERVICE,(res,chunk)=>res.write(chunk),"res.write.firstFoldCss");const tracedResWriteFirstFoldJS=(0,_otel.withSyncObservability)(SSR_SERVICE,(res,chunk)=>res.write(chunk),"res.write.firstFoldJS");const tracedResEnd=(0,_otel.withSyncObservability)(SSR_SERVICE,res=>res.end(),"res.end");const renderMarkUp=async(errorCode,req,res,metaTags,fetcherData,store,matches,context,webExtractor)=>{const deviceDetails=(0,_userAgentUtil.getUserAgentDetails)(req.headers["user-agent"]||"");const isBot=deviceDetails.googleBot||deviceDetails.aiBot?true:false;const safeArea=parseSafeAreaFromHeaders(req)||{...DEFAULT_SAFE_AREA_INSETS};const nativeWebView=isNativeWebViewRequest(req);/* eslint-disable no-undef */const previousSafeArea=globalThis.__SAFE_AREA_INITIAL__;const previousNativeWebView=globalThis.__CATALYST_NATIVE_WEBVIEW__;globalThis.__SAFE_AREA_INITIAL__=safeArea;globalThis.__CATALYST_NATIVE_WEBVIEW__=nativeWebView;/* eslint-enable no-undef */let state=store.getState();// collectChunks wraps the component tree so the extractor can track which loadable
20
- // chunks are actually rendered, building the final script/link tag lists.
21
- const jsx=webExtractor.collectChunks(getComponent(store,context,req,fetcherData));const{IS_DEV_COMMAND,WEBPACK_DEV_SERVER_HOSTNAME,WEBPACK_DEV_SERVER_PORT}=process.env;let publicAssetPath=`${process.env.PUBLIC_STATIC_ASSET_URL}${process.env.PUBLIC_STATIC_ASSET_PATH}`;// serves assets from localhost on running devBuild and devServe command
22
- if(IS_DEV_COMMAND==="true"){publicAssetPath=`http://${WEBPACK_DEV_SERVER_HOSTNAME}:${WEBPACK_DEV_SERVER_PORT}/assets/`;}const finalProps={req,res,lang:"en",pageCss:res.locals.pageCss,preloadJSLinks:res.locals.preloadJSLinks,metaTags,isBot,publicAssetPath,jsx,initialState:state,fetcherData,safeArea,nativeWebView};let CompleteDocument=()=>{if((0,_validator.validateCustomDocument)(_document2.default)){return(0,_document2.default)(finalProps);}else{return/*#__PURE__*/(0,_jsxRuntime.jsxs)("html",{lang:finalProps.lang,children:[/*#__PURE__*/(0,_jsxRuntime.jsx)(_document.Head,{isBot:finalProps.isBot,pageCss:finalProps.pageCss,metaTags:finalProps.metaTags,preloadJSLinks:finalProps.preloadJSLinks,publicAssetPath:finalProps.publicAssetPath}),/*#__PURE__*/(0,_jsxRuntime.jsx)(_document.Body,{jsx:finalProps.jsx,fetcherData:finalProps.fetcherData,initialState:finalProps.initialState,safeArea:finalProps.safeArea,nativeWebView:finalProps.nativeWebView})]});}};const cleanupGlobalThis=()=>{/* eslint-disable no-undef */if(previousSafeArea===undefined){delete globalThis.__SAFE_AREA_INITIAL__;}else{globalThis.__SAFE_AREA_INITIAL__=previousSafeArea;}if(previousNativeWebView===undefined){delete globalThis.__CATALYST_NATIVE_WEBVIEW__;}else{globalThis.__CATALYST_NATIVE_WEBVIEW__=previousNativeWebView;}/* eslint-enable no-undef */};try{let status=errorCode||(matches.length&&matches[0].match.path==="*"?404:200);res.set({"content-type":"text/html; charset=utf-8"});res.status(status);return new Promise((resolve,reject)=>{const{pipe}=(0,_server3.renderToPipeableStream)(/*#__PURE__*/(0,_jsxRuntime.jsx)(CompleteDocument,{}),{onShellReady(){// Start streaming the HTML shell (everything above Suspense boundaries)
23
- // as soon as it's ready to minimise TTFB.
24
- res.setHeader("content-type","text/html");pipe(res);},onAllReady(){// All Suspense boundaries have resolved. Append inline CSS and script
25
- // tags at the end of the stream so the browser can start executing JS.
26
- const{firstFoldCss,firstFoldJS}=(0,_extract.cacheAndFetchAssets)({webExtractor,res,isBot});tracedResWriteFirstFoldCss(res,firstFoldCss);tracedResWriteFirstFoldJS(res,firstFoldJS);tracedResEnd(res);cleanupGlobalThis();resolve();},onError(error){logger.error({message:`\n Error while renderToPipeableStream : ${error.toString()}`});// function defined by user which needs to run if rendering fails
27
- (0,_validator.safeCall)(onRenderError,{req,res,store,error});cleanupGlobalThis();reject(error);}});});}catch(error){logger.error({message:`Error in rendering document on server - ${error}`,trace:error.stack,url:req.originalUrl});// function defined by user which needs to run if rendering fails
28
- (0,_validator.safeCall)(onRenderError,{req,res,store,error});cleanupGlobalThis();return Promise.reject(error);}};const tracedAppServerSideFunction=(0,_otel.withObservability)(SSR_SERVICE,args=>_index.default.serverSideFunction(args),"App.serverSideFunction");const tracedServerDataFetcher=(0,_otel.withObservability)(SSR_SERVICE,_router.serverDataFetcher,"serverDataFetcher");const tracedRenderMarkUp=(0,_otel.withObservability)(SSR_SERVICE,renderMarkUp,"renderMarkUp");const tracedGetMetaData=(0,_otel.withSyncObservability)(SSR_SERVICE,_router.getMetaData,"getMetaData");/**
29
- * SSR request handler. Execution pipeline per request:
30
- * 1. Match route → collect loadable chunks (Phase 1 dry-run)
31
- * 2. App.serverSideFunction (app-level server hook)
32
- * 3. serverDataFetcher (route-level data fetching)
33
- * 4. renderMarkUp → renderToPipeableStream (Phase 2 — real render + stream to client)
34
- *
35
- * res.headersSent is checked after each async step: if a user hook (onRouteMatch,
36
- * onAppServerSideSuccess, etc.) has already sent a response (e.g. a redirect), we
37
- * bail out early without attempting another render.
38
- */async function _handler(req,res){try{let context={};let fetcherData={};const store=(0,_validator.validateConfigureStore)(createStore)?createStore({},req,res):null;const routes=getCachedRoutes();// matches: routes that exactly match this URL — used for serverSideFunction execution.
39
- // allMatches: full nested match tree — used by getMetaData to collect meta tags.
40
- const matches=getMatchRoutes(routes,req,res,store,context,fetcherData);const allMatches=(0,_router.matchRoutes)(routes,req.baseUrl);let allTags=[];// Extractor is attached to res.locals inside getMatchRoutes; reused for collectChunks in renderMarkUp
41
- const webExtractor=res.locals.webExtractor;(0,_validator.safeCall)(onRouteMatch,{req,res,matches,store});if(res.headersSent){return Promise.resolve(res);}try{await tracedAppServerSideFunction({store,req,res});(0,_validator.safeCall)(onAppServerSideSuccess,{req,res,store});if(res.headersSent){return Promise.resolve(res);}try{fetcherData=await tracedServerDataFetcher({routes:routes,req,res,url:req.originalUrl},{store});if(res.headersSent){return Promise.resolve(res);}// Check if serverDataFetcher returned an error in the response
42
- const err=fetcherData?.[req.originalUrl]?.error;allTags=tracedGetMetaData(allMatches,fetcherData);if(err){(0,_validator.safeCall)(onFetcherError,{req,res,store,error:err});if(res.headersSent){return Promise.reject(err);}// TODO: this seems very dependent on developers error handling, we are assuming they attach status_code key in their errors (same in App.serverSideFunction error)
43
- const statusCode=err.status_code||404;return new Promise((resolve,reject)=>{tracedRenderMarkUp(statusCode,req,res,allTags,fetcherData,store,matches,context,webExtractor).then(resolve).catch(reject);});}else{// function defined by user which needs to run after SSR functions are executed successfully
44
- (0,_validator.safeCall)(onFetcherSuccess,{req,res,store});if(res.headersSent){return Promise.resolve(res);}return new Promise((resolve,reject)=>{tracedRenderMarkUp(null,req,res,allTags,fetcherData,store,matches,context,webExtractor).then(resolve).catch(reject);});}}catch(error){// TODO: This catch block is kept for any unexpected errors from serverDataFetcher or getMetaData
45
- logger.error("Error in executing serverFetcher functions: "+error);(0,_validator.safeCall)(onFetcherError,{req,res,store,error});if(res.headersSent){return Promise.reject(error);}return new Promise((resolve,reject)=>{tracedRenderMarkUp(404,req,res,allTags,fetcherData,store,matches,context,webExtractor).then(resolve).catch(reject);});}}catch(error){logger.error("Error in executing serverSideFunction inside App: "+error);// function defined by user which needs to run after app server side function fails
46
- (0,_validator.safeCall)(onAppServerSideError,{req,res,store,error});if(res.headersSent){return Promise.reject(error);}return new Promise((resolve,reject)=>{tracedRenderMarkUp(error.status_code,req,res,allTags,fetcherData,store,matches,context,webExtractor).then(resolve).catch(reject);});}}catch(error){logger.error("Error in handling document request: "+error.toString());// function defined by user which needs to run when an error occurs in the handler
47
- (0,_validator.safeCall)(onRequestError,{req,res,error});return Promise.reject(error);}}const handler=(0,_otel.withObservability)(SSR_SERVICE,_handler,"handler");var _default=exports.default=handler;