@gooddata/sdk-ui-pluggable-host 11.40.0-alpha.3

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 (126) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +20 -0
  3. package/esm/assets/logo-white.svg +3 -0
  4. package/esm/components/FullScreenLoader.d.ts +1 -0
  5. package/esm/components/FullScreenLoader.js +8 -0
  6. package/esm/components/HostUiContainer.d.ts +16 -0
  7. package/esm/components/HostUiContainer.js +141 -0
  8. package/esm/components/HostUiContainer.scss +5 -0
  9. package/esm/components/Root.d.ts +16 -0
  10. package/esm/components/Root.js +64 -0
  11. package/esm/components/Root.scss +14 -0
  12. package/esm/components/lib/translations.d.ts +7 -0
  13. package/esm/components/lib/translations.js +64 -0
  14. package/esm/components/useRedirectNavigation.d.ts +7 -0
  15. package/esm/components/useRedirectNavigation.js +23 -0
  16. package/esm/components/useRedirectTarget.d.ts +19 -0
  17. package/esm/components/useRedirectTarget.js +62 -0
  18. package/esm/debug.d.ts +9 -0
  19. package/esm/debug.js +18 -0
  20. package/esm/index.d.ts +11 -0
  21. package/esm/index.js +10 -0
  22. package/esm/lib/chunkReloadGuard.d.ts +89 -0
  23. package/esm/lib/chunkReloadGuard.js +203 -0
  24. package/esm/lib/hostNotifications.d.ts +20 -0
  25. package/esm/lib/hostNotifications.js +50 -0
  26. package/esm/lib/isProduction.d.ts +12 -0
  27. package/esm/lib/isProduction.js +13 -0
  28. package/esm/loader/lastVisitedApp.d.ts +11 -0
  29. package/esm/loader/lastVisitedApp.js +43 -0
  30. package/esm/loader/localLoader.d.ts +16 -0
  31. package/esm/loader/localLoader.js +38 -0
  32. package/esm/loader/pluggableApplicationsLoader.d.ts +13 -0
  33. package/esm/loader/pluggableApplicationsLoader.js +55 -0
  34. package/esm/loader/redirectLogic.d.ts +30 -0
  35. package/esm/loader/redirectLogic.js +143 -0
  36. package/esm/loader/remoteLoader.d.ts +5 -0
  37. package/esm/loader/remoteLoader.js +117 -0
  38. package/esm/loader/remoteUrlSecurity.d.ts +1 -0
  39. package/esm/loader/remoteUrlSecurity.js +26 -0
  40. package/esm/loader/routing.d.ts +22 -0
  41. package/esm/loader/routing.js +87 -0
  42. package/esm/platformContext/backend.d.ts +44 -0
  43. package/esm/platformContext/backend.js +131 -0
  44. package/esm/platformContext/bootstrap.d.ts +15 -0
  45. package/esm/platformContext/bootstrap.js +122 -0
  46. package/esm/platformContext/loadPlatformContext.d.ts +18 -0
  47. package/esm/platformContext/loadPlatformContext.js +50 -0
  48. package/esm/platformContext/tigerNotAuthenticatedHandler.d.ts +3 -0
  49. package/esm/platformContext/tigerNotAuthenticatedHandler.js +16 -0
  50. package/esm/platformContext/types.d.ts +17 -0
  51. package/esm/platformContext/types.js +2 -0
  52. package/esm/platformContext/useLoadPlatformContext.d.ts +35 -0
  53. package/esm/platformContext/useLoadPlatformContext.js +131 -0
  54. package/esm/platformContext/useWorkspacePermissions.d.ts +26 -0
  55. package/esm/platformContext/useWorkspacePermissions.js +52 -0
  56. package/esm/platformContext/useWorkspaceSettings.d.ts +25 -0
  57. package/esm/platformContext/useWorkspaceSettings.js +46 -0
  58. package/esm/registry/pluggableApplicationsRegistry.d.ts +55 -0
  59. package/esm/registry/pluggableApplicationsRegistry.js +203 -0
  60. package/esm/sdk-ui-pluggable-host.d.ts +262 -0
  61. package/esm/styles/global.css +16 -0
  62. package/esm/translations/de-DE.json +34 -0
  63. package/esm/translations/en-AU.json +34 -0
  64. package/esm/translations/en-GB.json +34 -0
  65. package/esm/translations/en-US.json +130 -0
  66. package/esm/translations/es-419.json +34 -0
  67. package/esm/translations/es-ES.json +34 -0
  68. package/esm/translations/fi-FI.json +34 -0
  69. package/esm/translations/fr-CA.json +34 -0
  70. package/esm/translations/fr-FR.json +34 -0
  71. package/esm/translations/id-ID.json +34 -0
  72. package/esm/translations/it-IT.json +34 -0
  73. package/esm/translations/ja-JP.json +34 -0
  74. package/esm/translations/ko-KR.json +34 -0
  75. package/esm/translations/nl-NL.json +34 -0
  76. package/esm/translations/pl-PL.json +34 -0
  77. package/esm/translations/pt-BR.json +34 -0
  78. package/esm/translations/pt-PT.json +34 -0
  79. package/esm/translations/ru-RU.json +34 -0
  80. package/esm/translations/sl-SI.json +34 -0
  81. package/esm/translations/th-TH.json +34 -0
  82. package/esm/translations/tr-TR.json +34 -0
  83. package/esm/translations/uk-UA.json +34 -0
  84. package/esm/translations/vi-VN.json +34 -0
  85. package/esm/translations/zh-HK.json +34 -0
  86. package/esm/translations/zh-Hans.json +34 -0
  87. package/esm/translations/zh-Hant.json +34 -0
  88. package/esm/tsdoc-metadata.json +11 -0
  89. package/esm/types/lifecycle.d.ts +18 -0
  90. package/esm/types/lifecycle.js +2 -0
  91. package/esm/ui/DefaultHostUi.d.ts +12 -0
  92. package/esm/ui/DefaultHostUi.js +101 -0
  93. package/esm/ui/DefaultHostUi.scss +8 -0
  94. package/esm/ui/GenAIChat.d.ts +43 -0
  95. package/esm/ui/GenAIChat.js +102 -0
  96. package/esm/ui/HostChrome.d.ts +19 -0
  97. package/esm/ui/HostChrome.js +115 -0
  98. package/esm/ui/HostChrome.scss +24 -0
  99. package/esm/ui/HostIntlProvider.d.ts +9 -0
  100. package/esm/ui/HostIntlProvider.js +13 -0
  101. package/esm/ui/HostNotificationDispatcher.d.ts +12 -0
  102. package/esm/ui/HostNotificationDispatcher.js +42 -0
  103. package/esm/ui/PluggableApplicationRenderer.d.ts +10 -0
  104. package/esm/ui/PluggableApplicationRenderer.js +100 -0
  105. package/esm/ui/PluggableApplicationRenderer.scss +29 -0
  106. package/esm/ui/SemanticSearch.d.ts +23 -0
  107. package/esm/ui/SemanticSearch.js +46 -0
  108. package/esm/ui/WorkspacePicker.d.ts +9 -0
  109. package/esm/ui/WorkspacePicker.js +29 -0
  110. package/esm/ui/appMenuItems.d.ts +17 -0
  111. package/esm/ui/appMenuItems.js +81 -0
  112. package/esm/ui/chromeHelpers.d.ts +17 -0
  113. package/esm/ui/chromeHelpers.js +29 -0
  114. package/esm/ui/hostChromeBem.d.ts +1 -0
  115. package/esm/ui/hostChromeBem.js +3 -0
  116. package/esm/ui/resolveHostUiModule.d.ts +8 -0
  117. package/esm/ui/resolveHostUiModule.js +22 -0
  118. package/esm/ui/useHostChromeChat.d.ts +29 -0
  119. package/esm/ui/useHostChromeChat.js +38 -0
  120. package/esm/ui/useHostChromePricing.d.ts +52 -0
  121. package/esm/ui/useHostChromePricing.js +37 -0
  122. package/esm/ui/useHostChromeSearch.d.ts +20 -0
  123. package/esm/ui/useHostChromeSearch.js +18 -0
  124. package/esm/ui/useHostChromeWorkspaceFeatures.d.ts +19 -0
  125. package/esm/ui/useHostChromeWorkspaceFeatures.js +36 -0
  126. package/package.json +114 -0
@@ -0,0 +1,203 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useMemo } from "react";
3
+ import { toPluggableApplicationWorkspacePermissions, } from "@gooddata/sdk-model";
4
+ const SUPPORTED_API_VERSION = "1.0";
5
+ let registeredLocalApplications;
6
+ /**
7
+ * Registers the local pluggable applications manifest. Called by the host or harness
8
+ * before rendering.
9
+ *
10
+ * @alpha
11
+ */
12
+ export function registerLocalApplications(registry) {
13
+ registeredLocalApplications = registry;
14
+ }
15
+ /**
16
+ * @internal - exported for testing
17
+ */
18
+ export function getRemoteRegistry(ctx) {
19
+ const raw = ctx.userSettings.registeredPluggableApplications;
20
+ if (raw?.apiVersion === undefined) {
21
+ return undefined;
22
+ }
23
+ if (raw.apiVersion !== SUPPORTED_API_VERSION) {
24
+ console.error(`[host-runtime/registry] Remote registry apiVersion "${raw.apiVersion}" is not supported. ` +
25
+ `Expected "${SUPPORTED_API_VERSION}". Remote registry will be ignored.`);
26
+ return undefined;
27
+ }
28
+ return raw;
29
+ }
30
+ function getLocalApplications() {
31
+ if (!registeredLocalApplications) {
32
+ return [];
33
+ }
34
+ if (registeredLocalApplications.apiVersion !== SUPPORTED_API_VERSION) {
35
+ console.error(`[host-runtime/registry] Local registry apiVersion "${registeredLocalApplications.apiVersion}" ` +
36
+ `is not supported. Expected "${SUPPORTED_API_VERSION}". Local registry will be ignored.`);
37
+ return [];
38
+ }
39
+ return registeredLocalApplications.applications;
40
+ }
41
+ function mergeApplications(localApps, remoteApps) {
42
+ const seenIds = new Set();
43
+ const merged = [];
44
+ for (const app of localApps) {
45
+ if (seenIds.has(app.id)) {
46
+ console.error(`[host-runtime/registry] Duplicate application ID "${app.id}" in local registry, skipping.`);
47
+ continue;
48
+ }
49
+ seenIds.add(app.id);
50
+ merged.push(app);
51
+ }
52
+ for (const app of remoteApps) {
53
+ if (seenIds.has(app.id)) {
54
+ console.error(`[host-runtime/registry] Duplicate application ID "${app.id}" from remote registry, ` +
55
+ `local application takes precedence.`);
56
+ continue;
57
+ }
58
+ seenIds.add(app.id);
59
+ merged.push(app);
60
+ }
61
+ return merged;
62
+ }
63
+ function applyOverrides(apps, overrides) {
64
+ if (!overrides || Object.keys(overrides).length === 0) {
65
+ return apps;
66
+ }
67
+ return apps.map((app) => {
68
+ const override = overrides[app.id];
69
+ if (!override) {
70
+ return app;
71
+ }
72
+ return { ...app, ...override };
73
+ });
74
+ }
75
+ function sortByMenuOrder(apps) {
76
+ return [...apps].sort((a, b) => a.menuOrder - b.menuOrder);
77
+ }
78
+ function filterByAllowedList(apps, allowedIds) {
79
+ if (allowedIds === undefined) {
80
+ return apps;
81
+ }
82
+ const allowedSet = new Set(allowedIds);
83
+ return apps.filter((app) => allowedSet.has(app.id));
84
+ }
85
+ function filterDisabled(apps) {
86
+ // isEnabled is optional but "true" is the default value
87
+ return apps.filter((app) => app.isEnabled === undefined || app.isEnabled);
88
+ }
89
+ /**
90
+ * Evaluates a Condition<T> against an actual value object.
91
+ *
92
+ * Supports plain object (implicit AND), $or, and $and forms.
93
+ */
94
+ function evaluateCondition(condition, actual) {
95
+ if (typeof condition === "object" && condition !== null && "$or" in condition) {
96
+ return condition.$or.some((c) => evaluateCondition(c, actual));
97
+ }
98
+ if (typeof condition === "object" && condition !== null && "$and" in condition) {
99
+ return condition.$and.every((c) => evaluateCondition(c, actual));
100
+ }
101
+ if (actual === undefined) {
102
+ return false;
103
+ }
104
+ const plain = condition;
105
+ const actualRecord = actual;
106
+ return Object.keys(plain).every((key) => {
107
+ const required = plain[key];
108
+ const actual = actualRecord[key];
109
+ // A missing setting is treated as falsy: undefined satisfies a requirement of false.
110
+ return actual === required || (actual === undefined && required === false);
111
+ });
112
+ }
113
+ function entitlementsToRecord(entitlements) {
114
+ return Object.fromEntries((entitlements ?? []).map((e) => [e.name, e.value === undefined ? true : e.value]));
115
+ }
116
+ function toPluggableApplicationOrganizationPermissions(permissions) {
117
+ return {
118
+ canManageOrganization: permissions.canManageOrganization ?? false,
119
+ canCreateDevToken: permissions.canCreateDevToken ?? false,
120
+ hasBaseUiAccess: permissions.hasBaseUiAccess ?? false,
121
+ };
122
+ }
123
+ function appMeetsRequirements({ requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, requiredEntitlements, }, ctx) {
124
+ if (requiredSettings !== undefined && !evaluateCondition(requiredSettings, ctx.settings)) {
125
+ return false;
126
+ }
127
+ if (requiredWorkspacePermissions !== undefined) {
128
+ const wsPerms = ctx.workspacePermissions
129
+ ? toPluggableApplicationWorkspacePermissions(ctx.workspacePermissions)
130
+ : undefined;
131
+ if (!evaluateCondition(requiredWorkspacePermissions, wsPerms)) {
132
+ return false;
133
+ }
134
+ }
135
+ if (requiredOrganizationPermissions !== undefined &&
136
+ !evaluateCondition(requiredOrganizationPermissions, toPluggableApplicationOrganizationPermissions(ctx.organizationPermissions ?? {}))) {
137
+ return false;
138
+ }
139
+ if (requiredEntitlements !== undefined) {
140
+ const entitlementMap = entitlementsToRecord(ctx.entitlements);
141
+ if (!evaluateCondition(requiredEntitlements, entitlementMap)) {
142
+ return false;
143
+ }
144
+ }
145
+ return true;
146
+ }
147
+ function filterByRequirements(apps, ctx) {
148
+ return apps.filter((app) => appMeetsRequirements(app, ctx));
149
+ }
150
+ function filterByScope(apps, scope) {
151
+ if (scope === undefined) {
152
+ return [];
153
+ }
154
+ return apps.filter((app) => app.applicationScope === scope);
155
+ }
156
+ /**
157
+ * Returns true when the platform context indicates that access to the standard (local) apps is not
158
+ * restricted. Otherwise, the user must have BASE_UI_ACCESS organization permission.
159
+ *
160
+ * BASE_UI_ACCESS is a platform-wide organization permission that gates visibility of all
161
+ * standard (local) applications. Remote applications are not subject to this check because
162
+ * they are registered explicitly by the tenant and are expected to manage their own access.
163
+ */
164
+ function filterLocalByBaseUiAccess(localApps, ctx) {
165
+ if (!ctx.userSettings.restrictBaseUi) {
166
+ return localApps;
167
+ }
168
+ return ctx.organizationPermissions?.hasBaseUiAccess ? localApps : [];
169
+ }
170
+ /**
171
+ * Resolves the final list of pluggable applications from the local and remote registries.
172
+ *
173
+ * Processing steps (in order):
174
+ * 1. Filter local apps by allowedStandardApplications list (if provided in remote registry)
175
+ * 2. Filter local apps by BASE_UI_ACCESS organization permission (if restrictBaseUi is set)
176
+ * 3. Merge local and remote applications - local apps are added first, duplicates (by ID) are skipped with console.error
177
+ * 4. Apply overrides from the remote registry to the merged list
178
+ * 5. Filter out disabled applications (isEnabled: false)
179
+ * 6. Filter by application scope - keep only apps whose applicationScope matches scope; if scope is undefined, no apps pass through
180
+ * 7. Filter by requirements - check requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, and requiredEntitlements
181
+ * 8. Sort by menuOrder (ascending)
182
+ *
183
+ * @param options - Resolution options; see {@link IResolveApplicationsOptions}
184
+ * @returns Filtered and sorted list of pluggable applications ready for display
185
+ *
186
+ * @internal - exported for testing
187
+ */
188
+ export function resolveApplications({ localApps, remoteRegistry, ctx, scope, }) {
189
+ const { applications: remoteApps, overrides, allowedStandardApplications } = remoteRegistry ?? {};
190
+ const filteredLocal = filterLocalByBaseUiAccess(filterByAllowedList(localApps, allowedStandardApplications), ctx);
191
+ return sortByMenuOrder(filterByRequirements(filterByScope(filterDisabled(applyOverrides(mergeApplications(filteredLocal, remoteApps ?? []), overrides)), scope), ctx));
192
+ }
193
+ /**
194
+ * Builds the resolved list of pluggable applications from the local and remote registries.
195
+ */
196
+ export function usePluggableApplications(ctx) {
197
+ return useMemo(() => resolveApplications({
198
+ localApps: getLocalApplications(),
199
+ remoteRegistry: getRemoteRegistry(ctx),
200
+ ctx,
201
+ scope: ctx.currentApplicationScope,
202
+ }), [ctx]);
203
+ }
@@ -0,0 +1,262 @@
1
+ import { IAnalyticalBackend } from '@gooddata/sdk-backend-spi';
2
+ import { IHostUiNotification } from '@gooddata/sdk-pluggable-application-model';
3
+ import { IPlatformContext } from '@gooddata/sdk-pluggable-application-model';
4
+ import { IPluggableApp } from '@gooddata/sdk-pluggable-application-model';
5
+ import { IPluggableAppTelemetryCallbacks } from '@gooddata/sdk-pluggable-application-model';
6
+ import { JSX } from 'react/jsx-runtime';
7
+ import { LocalPluggableApplicationsRegistry } from '@gooddata/sdk-model';
8
+ import { ReactNode } from 'react';
9
+
10
+ /**
11
+ * Dispatches a notification into the currently mounted host UI module.
12
+ *
13
+ * @remarks
14
+ * If no UI is mounted yet (e.g. the host bootstrap is still in progress) the notification
15
+ * is queued and replayed when the host UI handle registers. The queue is capped at a small
16
+ * bounded size; oldest entries are dropped on overflow.
17
+ *
18
+ * @alpha
19
+ */
20
+ export declare function dispatchHostNotification(notification: IHostUiNotification): void;
21
+
22
+ /**
23
+ * @alpha
24
+ */
25
+ export declare function getBackend(): IAnalyticalBackend;
26
+
27
+ /**
28
+ * @alpha
29
+ */
30
+ export declare interface IAppLifecycleCallbacks {
31
+ onHostUiMounted?: (durationMs: number) => void;
32
+ onAppNavigation?: (appId: string, pathname: string) => void;
33
+ onPageVisited?: (appId: string) => void;
34
+ onPreloadStarted?: (appId: string) => void;
35
+ onPreloadCompleted?: (appId: string, durationMs: number) => void;
36
+ onLoadStarted?: (appId: string) => void;
37
+ onLoadCompleted?: (appId: string, durationMs: number) => void;
38
+ onMountCompleted?: (appId: string, durationMs: number) => void;
39
+ onRendered?: (appId: string, totalDurationMs: number) => void;
40
+ onLoadFailed?: (appId: string, error: string) => void;
41
+ onUnmounted?: (appId: string) => void;
42
+ createTelemetryCallbacks?: (appId: string) => IPluggableAppTelemetryCallbacks;
43
+ }
44
+
45
+ /**
46
+ * Shape returned by a pricing extension hook. Provides the trial-info derived from
47
+ * entitlements + a dialog element so the host chrome can wire the upsell affordances
48
+ * on `<AppHeader>` without owning the dialog implementation.
49
+ *
50
+ * @alpha
51
+ */
52
+ export declare interface IHostChromePricing {
53
+ /** The pricing dialog React element (or null to skip rendering). */
54
+ element: ReactNode;
55
+ /** Whether the current org is on a trial entitlement (drives upsell button visibility). */
56
+ isTrial: boolean;
57
+ /** ISO date string for the trial contract expiry, derived from entitlements. */
58
+ expiredDate: string;
59
+ /** Open the pricing dialog (wired to the upsell button click). */
60
+ onUpsellButtonClick: () => void;
61
+ }
62
+
63
+ /**
64
+ * @alpha
65
+ */
66
+ export declare interface ILoadPlatformContextCallbacks {
67
+ onBootstrapError?: (error: string, context: string) => void;
68
+ onLoaded?: (durationMs: number) => void;
69
+ }
70
+
71
+ /**
72
+ * Installs a window listener for `vite:preloadError`. When fired (e.g. a chunk has
73
+ * been removed by a redeploy), the page is hard-reloaded once to pick up new chunk
74
+ * hashes from a fresh index.html.
75
+ *
76
+ * Idempotent: calling more than once registers the listener only once.
77
+ *
78
+ * @alpha
79
+ */
80
+ export declare function installPreloadErrorHandler(): void;
81
+
82
+ /**
83
+ * Periodically polls the COMMITHASH file and fires `onNewDeployment` once when the
84
+ * deployed commit differs from the one the tab was loaded with. Stops polling
85
+ * after the first detection — the caller decides what to do (typically: show a
86
+ * "please reload" banner).
87
+ *
88
+ * Idempotent: calling more than once is a no-op after the first invocation.
89
+ *
90
+ * @alpha
91
+ */
92
+ export declare function installVersionWatcher({ url, intervalMs, onNewDeployment }: IVersionWatcherOptions): void;
93
+
94
+ /**
95
+ * @alpha
96
+ */
97
+ export declare interface IRootCallbacks {
98
+ onReady?: (ctx: IPlatformContext) => void;
99
+ onError?: (error: string, context: string) => void;
100
+ }
101
+
102
+ /**
103
+ * Optional callback invoked synchronously immediately before a stale-chunk hard reload.
104
+ * Gives the host app a chance to record telemetry before navigation cancels in-flight requests.
105
+ *
106
+ * @alpha
107
+ */
108
+ export declare interface IStaleChunkReloadInfo {
109
+ /** Human-readable reason — e.g. the underlying preload error message. */
110
+ reason: string;
111
+ /** COMMITHASH of the build the tab was loaded with, or an empty string if unknown. */
112
+ commitHash: string;
113
+ }
114
+
115
+ /**
116
+ * Options for {@link installVersionWatcher}.
117
+ *
118
+ * @alpha
119
+ */
120
+ export declare interface IVersionWatcherOptions {
121
+ /** URL of the COMMITHASH file emitted by the host build. */
122
+ url: string;
123
+ /** Poll interval in ms. Default: 5 minutes. */
124
+ intervalMs?: number;
125
+ /** Called once when a new deployment is detected. */
126
+ onNewDeployment: (newHash: string) => void;
127
+ }
128
+
129
+ /**
130
+ * @alpha
131
+ */
132
+ export declare type LocalPluggableApplicationLoader = () => Promise<{
133
+ default?: IPluggableApp | unknown;
134
+ pluggableApp?: IPluggableApp;
135
+ }>;
136
+
137
+ /**
138
+ * Registers app lifecycle callbacks used by the loader (e.g. for preload telemetry).
139
+ * Called by the host or harness at startup.
140
+ *
141
+ * @alpha
142
+ */
143
+ export declare function registerAppLifecycleCallbacks(callbacks: IAppLifecycleCallbacks): void;
144
+
145
+ /**
146
+ * Registers the local application loaders map. Called by the host or harness
147
+ * before any app loading occurs.
148
+ *
149
+ * @alpha
150
+ */
151
+ export declare function registerLocalApplicationLoaders(loaders: Record<string, LocalPluggableApplicationLoader>): void;
152
+
153
+ /**
154
+ * Registers the local pluggable applications manifest. Called by the host or harness
155
+ * before rendering.
156
+ *
157
+ * @alpha
158
+ */
159
+ export declare function registerLocalApplications(registry: LocalPluggableApplicationsRegistry): void;
160
+
161
+ /**
162
+ * Registers the host's platform-context lifecycle callbacks. Must be called once at
163
+ * host boot before `<Root>` is rendered so the very first load can report telemetry
164
+ * / errors back to the host application.
165
+ *
166
+ * @alpha
167
+ */
168
+ export declare function registerPlatformContextCallbacks(callbacks: ILoadPlatformContextCallbacks): void;
169
+
170
+ /**
171
+ * Triggers a hard page reload once, guarded against loops by sessionStorage.
172
+ *
173
+ * If the same COMMITHASH already triggered a reload within the last 30 seconds,
174
+ * the call is a no-op so the user is not stuck reloading a broken build.
175
+ *
176
+ * @remarks
177
+ * Uses `location.replace` with a cache-busting query parameter rather than
178
+ * `location.reload()`. A soft reload honours the HTTP cache, which has bitten
179
+ * us when an intermediate chunk (max-age=30d) is still served from cache and
180
+ * keeps replaying a stale module graph after the deploy moved forward. Writing
181
+ * a unique URL forces the browser to treat it as a fresh navigation.
182
+ *
183
+ * @alpha
184
+ */
185
+ export declare function reloadForStaleChunks(reason: string): void;
186
+
187
+ /**
188
+ * @alpha
189
+ */
190
+ export declare function Root({ callbacks }: {
191
+ callbacks?: IRootCallbacks;
192
+ }): JSX.Element;
193
+
194
+ /**
195
+ * Registers a pricing extension that the default host chrome will consume to render
196
+ * trial-upgrade affordances. Optional — when no extension is registered, the chrome
197
+ * shows no upsell button and no pricing dialog.
198
+ *
199
+ * @remarks
200
+ * This indirection keeps the chrome free of any hard dependency on a specific pricing
201
+ * dialog implementation (e.g. `gdc-pricing-dialog`). The host app supplies
202
+ * the extension at boot; removing the extension is a one-line change.
203
+ *
204
+ * Contract:
205
+ * - MUST be called once before the first render of `<Root>`.
206
+ * - The supplied function MUST obey React Rules of Hooks — it is invoked from
207
+ * `HostChrome`'s render.
208
+ * - Swapping or unregistering the extension after first render is unsupported and
209
+ * will violate Rules of Hooks.
210
+ *
211
+ * @alpha
212
+ */
213
+ export declare function setHostPricingExtension(extension: UseHostPricingExtension | undefined): void;
214
+
215
+ /**
216
+ * Sets the package name reported to the backend for telemetry.
217
+ * Must be called before the first backend request (i.e., before Root renders).
218
+ *
219
+ * @alpha
220
+ */
221
+ export declare function setRuntimePackageName(name: string): void;
222
+
223
+ /**
224
+ * Registers a listener invoked synchronously just before a stale-chunk hard reload,
225
+ * intended for telemetry. Called only when the reload actually happens — skipped
226
+ * reloads (loop-guard hits) do not fire the listener.
227
+ *
228
+ * @remarks
229
+ * Trackers should send the event via `navigator.sendBeacon` or `fetch` with `keepalive`
230
+ * because the page is navigating away immediately afterwards.
231
+ *
232
+ * @alpha
233
+ */
234
+ export declare function setStaleChunkReloadListener(listener: StaleChunkReloadListener | undefined): void;
235
+
236
+ /**
237
+ * Query-string parameter appended to the URL on a stale-chunk reload. The value
238
+ * is a timestamp; its only purpose is to make the navigation target a URL the
239
+ * browser has never seen before, defeating any cached HTML/remoteEntry that
240
+ * might otherwise replay the stale module graph.
241
+ *
242
+ * Exported so callers and tests can recognise (and strip) the marker if needed.
243
+ *
244
+ * @alpha
245
+ */
246
+ export declare const STALE_CHUNK_RELOAD_PARAM = "_gdcr";
247
+
248
+ /**
249
+ * @alpha
250
+ */
251
+ export declare type StaleChunkReloadListener = (info: IStaleChunkReloadInfo) => void;
252
+
253
+ /**
254
+ * A host pricing extension. The `Use` prefix signals that this MUST be a React hook —
255
+ * it is invoked from `HostChrome`'s render and therefore must obey the React Rules of
256
+ * Hooks (stable call order, no conditional hook calls, etc.).
257
+ *
258
+ * @alpha
259
+ */
260
+ export declare type UseHostPricingExtension = (ctx: IPlatformContext, locale: string) => IHostChromePricing;
261
+
262
+ export { }
@@ -0,0 +1,16 @@
1
+ /* (C) 2026 GoodData Corporation */
2
+
3
+ /*
4
+ * Base typography for the host app. Portaled components (GenAIChat, dialogs)
5
+ * render at document.body level and inherit these rules. Without them, browser
6
+ * defaults (16px / normal) apply, causing buttons and text to be oversized.
7
+ */
8
+ html,
9
+ body {
10
+ font-family: var(--gd-font-family), gdcustomfont, Avenir, "Helvetica Neue", arial, sans-serif;
11
+ font-size: 14px;
12
+ line-height: 1.4rem;
13
+ color: var(--gd-palette-complementary-8, #464e56);
14
+ margin: 0;
15
+ padding: 0;
16
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "gs.header.help": "Hilfe",
3
+ "gs.header.help.label": "Hilfe-Links",
4
+ "gs.header.logout": "Abmelden",
5
+ "gs.header.menu": "Menü",
6
+ "gs.header.accessibility.label": "Globale Anwendungskopfzeile",
7
+ "gs.header.href.accessibility": "Zur Startseite",
8
+ "gs.header.logo.title.accessibility": "{organizationName} logo",
9
+ "gs.header.mainMenu.ariaLabel": "Hauptmenü",
10
+ "gs.header.menu.accessibility.label": "Globale Navigation",
11
+ "gs.header.account.title": "Konto",
12
+ "gs.header.slack": "Slack",
13
+ "gs.header.community": "Gemeinschaft",
14
+ "gs.header.university": "Universität",
15
+ "gs.header.documentation": "Dokumentation",
16
+ "gs.host.error.applicationFailedToLoad": "Anwendung konnte nicht geladen werden",
17
+ "gs.host.error.failedToLoad": "Konnte nicht geladen werden",
18
+ "gs.host.error.pageNotFound": "Seite nicht gefunden",
19
+ "gs.host.error.pageNotFoundDescription": "Die Seite, die Sie suchen, existiert nicht oder Sie haben keinen Zugriff darauf.",
20
+ "gs.host.error.somethingWentWrong": "Ein unvorhergesehener Fehler ist aufgetreten.",
21
+ "gs.host.notification.newDeployment.message": "Eine neue Version von GoodData ist verfügbar.",
22
+ "gs.host.notification.newDeployment.reloadLink": "Neu laden",
23
+ "gs.header.helpMenu.gettingStarted": "Erste Schritte",
24
+ "gs.header.helpMenu.connectData": "Daten verbinden",
25
+ "gs.header.helpMenu.manage.ws": "Workspaces und Workspace-Hierarchie verwalten",
26
+ "gs.header.helpMenu.manage.user": "Benutzer und Benutzergruppen verwalten",
27
+ "messages.genAi.visualisation.saved.success": "Großartig! Wir haben Ihre Visualisierung gespeichert.",
28
+ "messages.genAi.visualisation.saved.error": "Hoppla, beim Speichern Ihrer Visualisierung ist ein Problem aufgetreten.",
29
+ "messages.genAi.visualisation.saved.error.detail": "{errorType}: {errorMessage}",
30
+ "messages.genAi.visualisation.link.copied": "Der Visualisierungslink wurde in Ihre Zwischenablage kopiert.",
31
+ "messages.showMore": "Mehr anzeigen",
32
+ "messages.showLess": "Weniger anzeigen",
33
+ "gen-ai.ask-assistant.search": "Erstellen Sie eine neue Visualisierung basierend auf: {question}"
34
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "gs.header.help": "Help",
3
+ "gs.header.help.label": "Help links",
4
+ "gs.header.logout": "Log out",
5
+ "gs.header.menu": "Menu",
6
+ "gs.header.accessibility.label": "Global application header",
7
+ "gs.header.href.accessibility": "Go to homepage",
8
+ "gs.header.logo.title.accessibility": "{organizationName} logo",
9
+ "gs.header.mainMenu.ariaLabel": "Main menu",
10
+ "gs.header.menu.accessibility.label": "Global navigation",
11
+ "gs.header.account.title": "Account",
12
+ "gs.header.slack": "Slack",
13
+ "gs.header.community": "Community",
14
+ "gs.header.university": "University",
15
+ "gs.header.documentation": "Documentation",
16
+ "gs.host.error.applicationFailedToLoad": "Application failed to load",
17
+ "gs.host.error.failedToLoad": "Failed to load",
18
+ "gs.host.error.pageNotFound": "Page not found",
19
+ "gs.host.error.pageNotFoundDescription": "The page you are looking for does not exist or you do not have access to it.",
20
+ "gs.host.error.somethingWentWrong": "Something went wrong",
21
+ "gs.host.notification.newDeployment.message": "A new version of GoodData is available.",
22
+ "gs.host.notification.newDeployment.reloadLink": "Reload",
23
+ "gs.header.helpMenu.gettingStarted": "Getting started",
24
+ "gs.header.helpMenu.connectData": "Connecting data",
25
+ "gs.header.helpMenu.manage.ws": "Managing workspaces and workspace hierarchy",
26
+ "gs.header.helpMenu.manage.user": "Managing users and user groups",
27
+ "messages.genAi.visualisation.saved.success": "Great! We've saved your visualisation.",
28
+ "messages.genAi.visualisation.saved.error": "Unfortunately, there was an issue when saving your visualisation.",
29
+ "messages.genAi.visualisation.saved.error.detail": "{errorType}: {errorMessage}",
30
+ "messages.genAi.visualisation.link.copied": "The visualisation link has been copied to your clipboard.",
31
+ "messages.showMore": "Show more",
32
+ "messages.showLess": "Show less",
33
+ "gen-ai.ask-assistant.search": "Create new visualisation based on: {question}"
34
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "gs.header.help": "Help",
3
+ "gs.header.help.label": "Assistance links",
4
+ "gs.header.logout": "Log out",
5
+ "gs.header.menu": "Menu",
6
+ "gs.header.accessibility.label": "Global application header",
7
+ "gs.header.href.accessibility": "Go to homepage",
8
+ "gs.header.logo.title.accessibility": "{organizationName} logo",
9
+ "gs.header.mainMenu.ariaLabel": "Main Menu",
10
+ "gs.header.menu.accessibility.label": "Global navigation",
11
+ "gs.header.account.title": "Account",
12
+ "gs.header.slack": "Slack",
13
+ "gs.header.community": "Community",
14
+ "gs.header.university": "University",
15
+ "gs.header.documentation": "Documentation",
16
+ "gs.host.error.applicationFailedToLoad": "Application failed to load",
17
+ "gs.host.error.failedToLoad": "Failed to load",
18
+ "gs.host.error.pageNotFound": "Page not found",
19
+ "gs.host.error.pageNotFoundDescription": "The page you are looking for does not exist or you do not have access to it.",
20
+ "gs.host.error.somethingWentWrong": "Something went wrong",
21
+ "gs.host.notification.newDeployment.message": "A new version of GoodData is available.",
22
+ "gs.host.notification.newDeployment.reloadLink": "Reload",
23
+ "gs.header.helpMenu.gettingStarted": "Getting started",
24
+ "gs.header.helpMenu.connectData": "Connecting data",
25
+ "gs.header.helpMenu.manage.ws": "Managing workspaces and workspace hierarchy",
26
+ "gs.header.helpMenu.manage.user": "Managing users and user groups",
27
+ "messages.genAi.visualisation.saved.success": "Great! We saved your visualisation.",
28
+ "messages.genAi.visualisation.saved.error": "An error occurred while saving your visualisation.",
29
+ "messages.genAi.visualisation.saved.error.detail": "{errorType}: {errorMessage}",
30
+ "messages.genAi.visualisation.link.copied": "The visualisation link has been copied to your clipboard.",
31
+ "messages.showMore": "Show more",
32
+ "messages.showLess": "Show less",
33
+ "gen-ai.ask-assistant.search": "Build new visualisation based on: {question}"
34
+ }