Prior attempts (raw JSON, \u007B / \u007D Unicode escapes) both got
truncated in the deployed build: Rspack's html-plugin decodes Unicode
escapes BEFORE running its template engine, so by the time the engine
sees the script body both raw and escaped '{z}' look identical and
get swallowed. Result: injected MAP_TILE_URL stopped at '/tile/{z'
and the client fell back to the default URL.
Serialize the env payload to base64 instead and decode it at runtime
with `JSON.parse(atob("..."))`. The base64 alphabet is A–Z/a–z/0–9/+//
/= — no braces for any template engine to grab. Switch the assign
target to `Object.create(null)` to keep the source brace-free; the
resulting runtime object is indistinguishable for getEnv().
Two gaps blocked http://flights-ui.devwebzavod.ru/ru/flights-map:
1. The inline <script>window.__ENV__=...</script> was written with the
Leaflet tile template ('/map/api/tile/{z}/{x}/{y}.jpeg') embedded
directly. Rspack's html-plugin pre-processes the children string and
ate the '{z}' placeholder, truncating the injected JS literal to
'/map/api/tile/{z' — MAP_TILE_URL on the client ended up broken and
getEnv() fell back to the default.
Escape every '{'/'}' inside the stringified value as '\u007B'/'\u007D'.
JS decodes the Unicode escapes back to '{}' at parse time; the html
plugin's template engine sees no placeholders to eat. Object-literal
braces outside the string stay raw (Unicode escapes aren't valid in
operator positions in JS source).
2. API_BASE_URL was still hard-defaulting to 'http://localhost:8080/api',
so every dictionary fetch on the deployed cluster died with
ERR_CONNECTION_REFUSED. Thread API_BASE_URL through the same
PUBLIC_ENV_KEYS/html.tags path as MAP_TILE_URL, add matching Docker
ARG/ENV, and forward it in deployment/build-docker.sh + k8s manifest.
The devwebzavod default for both is https://flights.test.aeroflot.ru
— where the real Aeroflot ingress terminates /map/api/** and /api/**.
Prod keeps overriding with same-origin URLs.
http://flights-ui.devwebzavod.ru/ru/flights-map was still hitting the
same-origin tile path after adding the k8s env: Modern.js renders the
<Suspense> fallback on the server (i18n isn't preloaded), so the route
component that reads getEnv() never actually runs during SSR. The page
hydrates client-side, where process.env is Rspack's empty stub and
MAP_TILE_URL is never set — getEnv() falls back to the default.
Move the value into window.__ENV__ instead:
- modern.config.ts: inline a <script> into html.tags that sets
window.__ENV__ = { MAP_TILE_URL: <value> } at SSR-server startup.
The snippet is authored once at server boot, so the HTML template
baked into dist/standalone/html/main/index.html always carries the
pod's tile URL.
- src/env/index.ts: merge window.__ENV__ on top of process.env so the
browser prefers the injected value (process.env only has NODE_ENV
after Rspack's polyfill).
- Dockerfile.react: accept MAP_TILE_URL as a build ARG and expose it
as ENV before `pnpm build:standalone`, so Modern.js picks it up when
building the HTML template. k8s env still flows into the Node SSR
process; the build-arg path guarantees correctness even when the
runtime env is stripped.
- deployment/build-docker.sh: forward MAP_TILE_URL through as a
build-arg (default keeps the same-origin path). CI on the
devwebzavod cluster can export MAP_TILE_URL=https://flights.test.aeroflot.ru/map/api/tile/{z}/{x}/{y}.jpeg
before running build-docker.sh and the resulting image will serve
tiles from the upstream the real Aeroflot ingress terminates.
Angular's index.html referenced /assets/img/favicon.ico + a PNG icon +
an apple-touch-icon; the React port carried those assets through
config/public/ but never linked them in the HTML head, so the tab icon
was blank and /favicon.ico 404'd. Add html.favicon (copied to publicDir
root) plus html.tags for 16/32 PNG icons and apple-touch-icon.
Three non-fatal warnings surfaced by the Jenkins build log, fixed in
the config layer:
- module-federation.config.ts: dts: false. The @module-federation/
dts-plugin fails under our strict tsconfig and logs TYPE-001 every
build. Remote types are not needed at runtime; consumers generate
their own via their dev toolchain.
- src/styles/_layout.scss: color-adjust → print-color-adjust. The
unprefixed color-adjust shorthand is deprecated; the standard name
is print-color-adjust. Matches the -webkit- prefixed sibling.
- modern.config.ts: tools.devServer.headers explicitly set. MF warns
when devServer.headers is empty and auto-assigns '*'. Providing an
explicit allowlist silences the banner in dev builds; production
behavior is unaffected (real reverse proxy manages CORS).
The v7_startTransition warning appeared because react-router 6.30.3
(bundled by Modern.js 2.70.8) emits future flag warnings by default.
A full Modern.js 2→3 upgrade was investigated but blocked by
@module-federation/modern-js ESM incompatibilities with Rsbuild 2.0
(uses __filename and require.resolve in ESM bundles, and the SSR
plugin calls api.modifyWebpackConfig which no longer exists).
Instead, opt into all v7 future flags via runtime.router config.
This silences the warning and prepares the codebase for an eventual
React Router v7 upgrade when the MF plugin catches up.
- Point API_BASE_URL to localhost:4200 (Angular's dev proxy) which
correctly forwards to flights.test.aeroflot.ru with proper headers
- Convert URL date format (yyyyMMdd) to API format (yyyy-MM-ddT00:00:00)
matching Angular's ApiFormatterService behavior
- Add standalone api-proxy.mjs script for running without Angular
- Root page redirect uses both loader and client-side navigate
- SignalR hub URL points to platform.yc.webzavod.ru/tracker/hub
- Remove broken server/modern-js.server.ts (proxy handled externally)
API functions now build the full localized path matching the Angular
EndpointService pattern (/api/flights/{version}/{locale}/{endpoint}).
The dev proxy forwards /api and /flights to the test backend.
- Copy 134 image files and 28 font files from ClientApp/src/assets/
to public/assets/ for browser-side serving
- Set tools.cssLoader.url=false in modern.config.ts so the CSS loader
leaves url() references as-is instead of trying to resolve them as
webpack modules
- Add .playwright-mcp/, coverage/, and screenshot artifacts to .gitignore
Replace Google Fonts CDN import with self-hosted @font-face declarations
from the Angular app, pointing to /assets/fonts/*.woff2 in public/.
Configure rspack css-loader to skip url() resolution so the browser
fetches assets from the dev server's public/ directory at runtime.
The server build was failing with "window is not defined" during SSR because
@modern-js/runtime@3.1.3 had an SSR-unsafe window reference in its router
plugin. Pinning runtime to 2.70.8 (matching app-tools) resolves the version
mismatch and eliminates the server-side window access.
Three root causes of blank page:
1. Modern.js layouts use <Outlet /> not {children} for nested routes
2. process.env not available in browser — guard with typeof checks
3. getEnv() schema required all fields — add defaults for browser context
Also: add source.entriesDir, runtime.router to modern.config.ts,
disable SSR temporarily until the SSR server build alias issue is
resolved (framework-level @_modern_js_src resolution).