@autobusal/providers 1.6.4 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@ All notable changes to `@autobusal/providers` are documented here. This project
4
4
  adheres to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.6.6] - 2026-07-31
8
+
9
+ ### Fixed
10
+
11
+ - **`Providers.tsx`: `Setup` (and its `useGetMenu()` call) now has its own
12
+ inner `<Suspense>` boundary**, instead of sharing the outer one with
13
+ `Recaptcha`/`Styles`/`Notifications`. `Setup` calls a second,
14
+ independently-resolving `useSuspenseQuery` (`get-menu`) after
15
+ `useGetSettings()`; on a cold menu cache this suspended AFTER
16
+ Recaptcha's `GoogleReCaptchaProvider` had already committed and
17
+ injected its script/badge - and a Suspense boundary that re-suspends
18
+ discards its already-committed content and remounts it once ready.
19
+ `GoogleReCaptchaProvider` isn't remount-safe (its script/badge
20
+ injection is async, outside React's own DOM bookkeeping), so the
21
+ remount left a duplicate, half-torn-down `.grecaptcha-badge` behind.
22
+ That corrupted DOM state then threw an uncaught `TypeError: Cannot
23
+ read properties of null (reading 'removeChild')` the next time React
24
+ reconciled anywhere near the root - i.e. on every subsequent route
25
+ change, since Recaptcha wraps the whole router. The exception isn't
26
+ caught by the app's `ErrorBoundary` (render-phase only), so it
27
+ silently aborted the commit: the URL would update (the router's
28
+ `history.pushState` already ran) but the page itself never
29
+ re-rendered - freezing client-side navigation app-wide, most visibly
30
+ reproduced via the account dropdown menu (Password, Telegram, and the
31
+ impersonation-aware Logout/"Return to my account" link, which is what
32
+ surfaced this).
33
+
34
+ ## [1.6.5] - 2026-07-31
35
+
36
+ ### Added
37
+
38
+ - **`Queries/apiClient.ts` sends `Content-Language`** on every request,
39
+ reflecting the live active i18next language. The API's
40
+ ChangeApiLanguage middleware/Languaged trait expect this to localize
41
+ city/country/article/... names, but the frontend never sent it -
42
+ those names always rendered in the API's own default locale
43
+ regardless of the UI's actual language. Requires obtapi's
44
+ Traits\Languaged fix (falls back safely for the 13 UI languages the
45
+ database has no columns for) and @autobusal/bff 1.2.0 (forwards the
46
+ header upstream in bff mode) to actually take effect end-to-end.
47
+
7
48
  ## [1.6.4] - 2026-07-30
8
49
 
9
50
  ### Added
package/Providers.tsx CHANGED
@@ -24,16 +24,38 @@ const Providers = ({ type, loading, children }: Props): JSX.Element => (
24
24
  // Recaptcha moved INSIDE Queries: it now reads the label's runtime captcha
25
25
  // settings via useGetSettings(), which needs the QueryClientProvider (and
26
26
  // suspends into the outer boundary on a cold cache, like Styles/Setup).
27
+ // Edited: Ferjolt Ozuni - Date: 2026-07-31
28
+ // Bug: Setup also calls useGetMenu() (a SEPARATE useSuspenseQuery, its own
29
+ // cache key/promise) in its own render body, still inside this same outer
30
+ // boundary. On a cold menu cache, that suspends AFTER Recaptcha/Styles/
31
+ // Notifications above it have already committed - and a Suspense boundary
32
+ // re-suspending after its content is already committed hides/discards that
33
+ // whole committed subtree and mounts it again once ready. Recaptcha's
34
+ // GoogleReCaptchaProvider isn't remount-safe: its script/badge injection is
35
+ // async and outside React's DOM bookkeeping, so remounting it left a
36
+ // duplicate, half-torn-down <div class="grecaptcha-badge"> behind. That
37
+ // corrupted DOM state then threw an uncaught "Cannot read properties of
38
+ // null (reading 'removeChild')" the next time React reconciled anywhere
39
+ // near the root - which is every route change, since Recaptcha wraps the
40
+ // whole router - silently aborting the commit. The URL would update (the
41
+ // router's history.pushState already ran) but the page itself would never
42
+ // re-render, freezing navigation app-wide with no console-visible error
43
+ // (uncaught DOM exceptions like this don't hit the render-phase-only
44
+ // ErrorBoundary). Giving Setup its own inner boundary means its menu
45
+ // suspension can only hide/retry ITSELF, not the already-committed
46
+ // Recaptcha/Styles/Notifications above it.
27
47
  <Suspense fallback={ loading }>
28
48
  <Queries>
29
49
  <Recaptcha>
30
50
  <Styles>
31
51
  <Notifications>
32
- <Setup type={ type }>
33
- <Suspense fallback={ loading }>
34
- { children }
35
- </Suspense>
36
- </Setup>
52
+ <Suspense fallback={ loading }>
53
+ <Setup type={ type }>
54
+ <Suspense fallback={ loading }>
55
+ { children }
56
+ </Suspense>
57
+ </Setup>
58
+ </Suspense>
37
59
  </Notifications>
38
60
  </Styles>
39
61
  </Recaptcha>
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios';
2
+ import i18next from 'i18next';
2
3
  import languages from '@lang/errors';
3
4
  import { getLanguage } from '../Setup/languages';
4
5
  import { error as systemError } from '@autobusal/utilities';
@@ -37,6 +38,24 @@ if (!isBffMode) {
37
38
  });
38
39
  }
39
40
 
41
+ // The API localizes city/country/article/... names off this header
42
+ // (ChangeApiLanguage middleware) - was never sent at all, so every one of
43
+ // those names always rendered in the API's own default locale regardless
44
+ // of the UI's actual language. i18next.language reflects the live active
45
+ // language (kept in sync by Setup/languages.ts on every change), read
46
+ // fresh per request rather than closed over once, so a language switch
47
+ // takes effect on the very next call with no extra wiring anywhere else.
48
+ // In bff mode this also needs public/bff/index.php's api() to forward it
49
+ // upstream (see @autobusal/bff) - both sides had to change, only one of
50
+ // them silently doing nothing would look identical to neither working.
51
+ apiClient.interceptors.request.use(config => {
52
+ if (i18next.language) {
53
+ config.headers['Content-Language'] = i18next.language;
54
+ }
55
+
56
+ return config;
57
+ });
58
+
40
59
  apiClient.interceptors.response.use(response => (
41
60
  response
42
61
  ), async (error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/providers",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
6
  "main": "index.ts"