@autobusal/providers 1.6.8 → 1.7.0
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 +35 -0
- package/Providers.tsx +86 -35
- package/package.json +1 -1
- package/types/locations.ts +16 -0
- package/types/settings.ts +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ 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.7.0] - 2026-07-31
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`LabelSettings.seo` / `SettingsData.seo`** (`types/settings.ts`): optional
|
|
12
|
+
homepage title/description/keywords override, admin-managed via the new
|
|
13
|
+
`@autobusal/admin-label` 1.9.0 SEO tab and persisted through obtapi's
|
|
14
|
+
`Labels\AdminController::seo()`. `SettingsData.seo` is the public-safe
|
|
15
|
+
subset exposed by `Settings\ViewController::get()` for `StaticHome.tsx`
|
|
16
|
+
to prefer over its i18n translation-template values when set.
|
|
17
|
+
- **`CountryData.seo_title`/`seo_description`/`seo_keywords` and the same
|
|
18
|
+
three fields on `CityData`** (`types/locations.ts`): optional per-entity
|
|
19
|
+
override for a country/city landing page's meta tags, admin-managed
|
|
20
|
+
through the existing Countries/Cities admin forms and persisted via
|
|
21
|
+
obtapi's `Countries\AdminController`/`Cities\AdminController`.
|
|
22
|
+
`LocationCountry.tsx`/`LocationCity.tsx` prefer these over their i18n
|
|
23
|
+
translation-template + live-stats content when set.
|
|
24
|
+
|
|
25
|
+
## [1.6.9] - 2026-07-31
|
|
26
|
+
|
|
27
|
+
### Added (re-added)
|
|
28
|
+
|
|
29
|
+
- **`useRecoverFromDomCorruption()`'s global `removeChild`/`insertBefore`
|
|
30
|
+
listener is back**, generalized. Removed in 1.6.8 as a backstop
|
|
31
|
+
specifically for `GoogleReCaptchaProvider`'s remount corruption, on the
|
|
32
|
+
assumption that removing that provider removed the trigger. It didn't:
|
|
33
|
+
`@autobusal/common` 1.8.3 fixed a second, independent cause
|
|
34
|
+
(`Meta.tsx`'s stale-tag cleanup racing React 19's own `<title>`/`<meta>`
|
|
35
|
+
hoisting), and the exact same uncaught error still reproduced
|
|
36
|
+
intermittently with BOTH fixes shipped - some remaining React/router
|
|
37
|
+
timing edge case neither touches. Reinstated as a permanent safety net
|
|
38
|
+
rather than a fix for one specific bug: whatever the next contributing
|
|
39
|
+
cause turns out to be, a reload of the current URL has recovered
|
|
40
|
+
cleanly in every reproduction so far.
|
|
41
|
+
|
|
7
42
|
## [1.6.8] - 2026-07-31
|
|
8
43
|
|
|
9
44
|
### Removed
|
package/Providers.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Suspense } from 'react';
|
|
1
|
+
import { Suspense, useEffect } from 'react';
|
|
2
2
|
import Queries from './Queries/Queries';
|
|
3
3
|
import Styles from './Styles/Styles';
|
|
4
4
|
import Notifications from './Notifications/Notifications';
|
|
@@ -10,39 +10,90 @@ interface Props {
|
|
|
10
10
|
children: JSX.Element
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
13
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
14
|
+
// Permanent safety net, not a fix for one specific bug. Two real, separate
|
|
15
|
+
// causes of a client-side navigation silently never landing were found and
|
|
16
|
+
// fixed - GoogleReCaptchaProvider's remount corruption (removed entirely,
|
|
17
|
+
// see this package's 1.6.6-1.6.8 and @autobusal/hooks 1.3.1) and Meta.tsx's
|
|
18
|
+
// stale-tag cleanup racing React 19's own <title>/<meta> hoisting (see
|
|
19
|
+
// @autobusal/common 1.8.3) - and the exact same uncaught "Cannot read
|
|
20
|
+
// properties of null (reading 'removeChild')" still reproduced
|
|
21
|
+
// intermittently with both gone: some remaining React/router timing edge
|
|
22
|
+
// case neither fix touches. That class of error is thrown outside React's
|
|
23
|
+
// render phase, so no ErrorBoundary catches it, and it silently aborts
|
|
24
|
+
// whatever navigation was in flight - the URL updates (the router's
|
|
25
|
+
// history.pushState already ran) but the page itself never re-renders, with
|
|
26
|
+
// no visible error and no way to navigate out. A reload of the current URL
|
|
27
|
+
// has recovered cleanly in every reproduction so far, so: watch for this
|
|
28
|
+
// exact error signature anywhere in the app and self-heal with one reload,
|
|
29
|
+
// cooldown-guarded so a genuinely persistent error can't loop. Logout.tsx/
|
|
30
|
+
// LoginAs.tsx carry a second, more targeted watchdog (a plain timer, not
|
|
31
|
+
// error-triggered) for the two pages users actually get stuck on during
|
|
32
|
+
// impersonation - this one is the general backstop for everywhere else.
|
|
33
|
+
const useRecoverFromDomCorruption = (): void => {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const RECOVERY_KEY = 'autobusalDomRecoveryAt';
|
|
36
|
+
|
|
37
|
+
const onError = (event: ErrorEvent): void => {
|
|
38
|
+
const message = event.message || '';
|
|
39
|
+
|
|
40
|
+
if (!message.includes('removeChild') && !message.includes('insertBefore')) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const last = Number(sessionStorage.getItem(RECOVERY_KEY) || 0);
|
|
45
|
+
|
|
46
|
+
if (Date.now() - last < 10000) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sessionStorage.setItem(RECOVERY_KEY, String(Date.now()));
|
|
51
|
+
window.location.reload();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
window.addEventListener('error', onError);
|
|
55
|
+
|
|
56
|
+
return () => window.removeEventListener('error', onError);
|
|
57
|
+
}, []);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const Providers = ({ type, loading, children }: Props): JSX.Element => {
|
|
61
|
+
useRecoverFromDomCorruption();
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
// Styles and Setup both call useGetSettings() (a useSuspenseQuery) and
|
|
65
|
+
// sit ABOVE the inner <Suspense> below, so on a cold settings cache
|
|
66
|
+
// their suspension had no boundary to catch it and React threw error
|
|
67
|
+
// #426 ("a component suspended while responding to synchronous input").
|
|
68
|
+
// This outer boundary catches those two suspensions (showing `loading`
|
|
69
|
+
// during the initial settings fetch). The QueryClient in <Queries> is
|
|
70
|
+
// created at module scope, so remounting this subtree while suspended
|
|
71
|
+
// keeps the same client and cache - no refetch loop.
|
|
72
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
73
|
+
// Setup also calls useGetMenu() (a SEPARATE useSuspenseQuery, its own
|
|
74
|
+
// cache key/promise) in its own render body. Kept in its own inner
|
|
75
|
+
// <Suspense> rather than sharing the outer one: a cold menu cache
|
|
76
|
+
// suspending after Styles/Notifications above it have already committed
|
|
77
|
+
// would otherwise hide/discard that already-committed content and
|
|
78
|
+
// remount it once ready - harmless for Styles/Notifications themselves,
|
|
79
|
+
// but a needless remount of anything stateful sitting there is worth
|
|
80
|
+
// avoiding on principle.
|
|
81
|
+
<Suspense fallback={ loading }>
|
|
82
|
+
<Queries>
|
|
83
|
+
<Styles>
|
|
84
|
+
<Notifications>
|
|
85
|
+
<Suspense fallback={ loading }>
|
|
86
|
+
<Setup type={ type }>
|
|
87
|
+
<Suspense fallback={ loading }>
|
|
88
|
+
{ children }
|
|
89
|
+
</Suspense>
|
|
90
|
+
</Setup>
|
|
91
|
+
</Suspense>
|
|
92
|
+
</Notifications>
|
|
93
|
+
</Styles>
|
|
94
|
+
</Queries>
|
|
95
|
+
</Suspense>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
47
98
|
|
|
48
99
|
export default Providers;
|
package/package.json
CHANGED
package/types/locations.ts
CHANGED
|
@@ -25,6 +25,14 @@ export interface CountryData {
|
|
|
25
25
|
iso_code: string
|
|
26
26
|
available?: number
|
|
27
27
|
stats?: CountryStats
|
|
28
|
+
|
|
29
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
30
|
+
// Optional hand-written override for the public country page's title/
|
|
31
|
+
// description/keywords - see LocationCountry.tsx for the fallback-to-
|
|
32
|
+
// template logic when these are unset.
|
|
33
|
+
seo_title?: string
|
|
34
|
+
seo_description?: string
|
|
35
|
+
seo_keywords?: string
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export interface CityStats {
|
|
@@ -62,6 +70,14 @@ export interface CityData {
|
|
|
62
70
|
image?: string[]
|
|
63
71
|
stats?: CityStats
|
|
64
72
|
destinations?: CityDestination[]
|
|
73
|
+
|
|
74
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
75
|
+
// Optional hand-written override for the public city page's title/
|
|
76
|
+
// description/keywords - see LocationCity.tsx for the fallback-to-
|
|
77
|
+
// template logic when these are unset.
|
|
78
|
+
seo_title?: string
|
|
79
|
+
seo_description?: string
|
|
80
|
+
seo_keywords?: string
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
export interface StopData {
|
package/types/settings.ts
CHANGED
|
@@ -18,6 +18,16 @@ export interface SettingsData {
|
|
|
18
18
|
telegram: { available: boolean, price: number }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
22
|
+
// Optional admin-managed override for the homepage's title/description/
|
|
23
|
+
// keywords (Labels\AdminController::seo()) - StaticHome.tsx prefers these
|
|
24
|
+
// over its i18n translation-template values when set.
|
|
25
|
+
seo?: {
|
|
26
|
+
title?: string
|
|
27
|
+
description?: string
|
|
28
|
+
keywords?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
preferences: {
|
|
22
32
|
company: string
|
|
23
33
|
|
|
@@ -194,6 +204,15 @@ export interface LabelSettings {
|
|
|
194
204
|
linkedin?: string
|
|
195
205
|
trustpilot?: string
|
|
196
206
|
}
|
|
207
|
+
|
|
208
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
209
|
+
// Admin-managed homepage SEO override (public seo? above is populated
|
|
210
|
+
// from this) - see Labels\AdminController::seo().
|
|
211
|
+
seo?: {
|
|
212
|
+
title?: string
|
|
213
|
+
description?: string
|
|
214
|
+
keywords?: string
|
|
215
|
+
}
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
// Edited: Ferjolt Ozuni - Date: 2026-07-18
|