@nsxbet/admin-sdk 0.7.0 → 0.8.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/CHECKLIST.md +48 -13
- package/README.md +24 -74
- package/dist/auth/client/bff.d.ts +38 -0
- package/dist/auth/client/bff.js +270 -0
- package/dist/auth/client/in-memory.d.ts +1 -1
- package/dist/auth/client/in-memory.js +2 -2
- package/dist/auth/client/index.d.ts +1 -1
- package/dist/auth/client/index.js +2 -2
- package/dist/auth/client/interface.d.ts +4 -4
- package/dist/auth/client/interface.js +1 -1
- package/dist/auth/client/private-network-guidance.d.ts +2 -0
- package/dist/auth/client/private-network-guidance.js +38 -0
- package/dist/auth/components/LoginPage.d.ts +8 -0
- package/dist/auth/components/LoginPage.js +32 -0
- package/dist/auth/components/UserSelector.d.ts +29 -0
- package/dist/auth/components/UserSelector.js +38 -10
- package/dist/auth/components/UserSelector.stories.d.ts +9 -0
- package/dist/auth/components/UserSelector.stories.js +70 -0
- package/dist/auth/components/index.d.ts +2 -0
- package/dist/auth/components/index.js +1 -0
- package/dist/auth/index.d.ts +3 -2
- package/dist/auth/index.js +2 -2
- package/dist/components/AuthProvider.d.ts +3 -3
- package/dist/components/AuthProvider.js +35 -17
- package/dist/env.d.ts +17 -0
- package/dist/env.js +50 -0
- package/dist/hooks/useAuth.d.ts +3 -3
- package/dist/hooks/useAuth.js +26 -21
- package/dist/hooks/useCallbackRef.d.ts +7 -0
- package/dist/hooks/useCallbackRef.js +14 -0
- package/dist/hooks/useFetch.js +6 -1
- package/dist/hooks/useI18n.js +2 -2
- package/dist/hooks/usePlatformAPI.d.ts +0 -3
- package/dist/hooks/usePlatformAPI.js +6 -4
- package/dist/i18n/config.d.ts +2 -1
- package/dist/i18n/config.js +4 -3
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/locales/en-US.json +7 -0
- package/dist/i18n/locales/es.json +7 -0
- package/dist/i18n/locales/pt-BR.json +7 -0
- package/dist/i18n/locales/ro.json +7 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +6 -2
- package/dist/registry/AdminShellRegistry.js +4 -3
- package/dist/registry/client/http.js +6 -1
- package/dist/registry/client/in-memory.js +20 -5
- package/dist/registry/types/manifest.d.ts +5 -0
- package/dist/registry/types/manifest.js +4 -1
- package/dist/registry/types/module.d.ts +6 -2
- package/dist/sdk-version.d.ts +5 -0
- package/dist/sdk-version.js +5 -0
- package/dist/shell/AdminShell.d.ts +12 -9
- package/dist/shell/AdminShell.js +56 -70
- package/dist/shell/components/ModuleOverview.js +1 -5
- package/dist/shell/components/RegistryPage.js +1 -1
- package/dist/shell/components/TopBar.js +2 -2
- package/dist/shell/components/theme-provider.js +6 -8
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/polling-config.d.ts +4 -3
- package/dist/shell/polling-config.js +11 -9
- package/dist/shell/types.d.ts +3 -1
- package/dist/types/platform.d.ts +2 -11
- package/dist/vite/config.d.ts +4 -9
- package/dist/vite/config.js +85 -27
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/plugins.js +6 -1
- package/package.json +20 -6
- package/scripts/write-sdk-version.mjs +21 -0
- package/dist/auth/client/keycloak.d.ts +0 -18
- package/dist/auth/client/keycloak.js +0 -129
- package/dist/shell/BackofficeShell.d.ts +0 -37
- package/dist/shell/BackofficeShell.js +0 -339
- package/dist/types/keycloak.d.ts +0 -25
- package/dist/types/keycloak.js +0 -1
package/dist/env.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime environment from `window.__ENV__` (set by `/env.js` from the shell or adminModule() dev server).
|
|
3
|
+
*/
|
|
4
|
+
function isValidUrl(value) {
|
|
5
|
+
try {
|
|
6
|
+
new URL(value);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function parseEnv() {
|
|
14
|
+
const raw = (typeof window !== "undefined" && window.__ENV__) || {};
|
|
15
|
+
let adminGatewayUrl;
|
|
16
|
+
if (typeof raw.ADMIN_GATEWAY_URL === "string" && raw.ADMIN_GATEWAY_URL) {
|
|
17
|
+
if (isValidUrl(raw.ADMIN_GATEWAY_URL)) {
|
|
18
|
+
adminGatewayUrl = raw.ADMIN_GATEWAY_URL;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
console.warn("[env] ADMIN_GATEWAY_URL is not a valid URL:", raw.ADMIN_GATEWAY_URL);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const mockAuthRaw = raw.MOCK_AUTH;
|
|
25
|
+
if (mockAuthRaw !== undefined &&
|
|
26
|
+
mockAuthRaw !== "true" &&
|
|
27
|
+
mockAuthRaw !== "false") {
|
|
28
|
+
console.warn('[env] MOCK_AUTH must be "true" or "false", got:', mockAuthRaw);
|
|
29
|
+
}
|
|
30
|
+
let registryPollInterval;
|
|
31
|
+
const rawPoll = raw.REGISTRY_POLL_INTERVAL;
|
|
32
|
+
if (rawPoll !== undefined && rawPoll !== null && rawPoll !== "") {
|
|
33
|
+
const parsed = Number(rawPoll);
|
|
34
|
+
if (!Number.isNaN(parsed)) {
|
|
35
|
+
registryPollInterval = parsed;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.warn("[env] REGISTRY_POLL_INTERVAL must be a number, got:", rawPoll);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
adminGatewayUrl,
|
|
43
|
+
mockAuth: mockAuthRaw === "true",
|
|
44
|
+
environment: typeof raw.ENVIRONMENT === "string" && raw.ENVIRONMENT
|
|
45
|
+
? raw.ENVIRONMENT
|
|
46
|
+
: "local",
|
|
47
|
+
registryPollInterval,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export const env = parseEnv();
|
package/dist/hooks/useAuth.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { User } from '../types/platform';
|
|
|
3
3
|
* Authentication methods available in all modes
|
|
4
4
|
*/
|
|
5
5
|
export interface UseAuthResult {
|
|
6
|
-
/** Get the current access token */
|
|
7
|
-
getAccessToken: () => Promise<string>;
|
|
6
|
+
/** Get the current access token. Null when using BFF HttpOnly cookie auth. */
|
|
7
|
+
getAccessToken: () => Promise<string | null>;
|
|
8
8
|
/** Check if user has a specific permission */
|
|
9
9
|
hasPermission: (permission: string) => boolean;
|
|
10
10
|
/** Get current user information */
|
|
@@ -15,6 +15,6 @@ export interface UseAuthResult {
|
|
|
15
15
|
/**
|
|
16
16
|
* useAuth provides authentication methods that work seamlessly across:
|
|
17
17
|
* - Shell mode (using platform API)
|
|
18
|
-
* - Standalone mode with AuthClient (
|
|
18
|
+
* - Standalone mode with AuthClient (BFF cookie or in-memory)
|
|
19
19
|
*/
|
|
20
20
|
export declare function useAuth(): UseAuthResult;
|
package/dist/hooks/useAuth.js
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified authentication hook that works in shell, standalone, and mock modes
|
|
3
3
|
*/
|
|
4
|
+
import { useMemo } from 'react';
|
|
4
5
|
import { usePlatformAPI } from './usePlatformAPI';
|
|
6
|
+
import { useCallbackRef } from './useCallbackRef';
|
|
5
7
|
import { useOptionalAuthContext } from '../components/AuthProvider';
|
|
6
8
|
/**
|
|
7
9
|
* useAuth provides authentication methods that work seamlessly across:
|
|
8
10
|
* - Shell mode (using platform API)
|
|
9
|
-
* - Standalone mode with AuthClient (
|
|
11
|
+
* - Standalone mode with AuthClient (BFF cookie or in-memory)
|
|
10
12
|
*/
|
|
11
13
|
export function useAuth() {
|
|
12
14
|
const { isShellMode, api } = usePlatformAPI();
|
|
13
15
|
const authContext = useOptionalAuthContext();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
const standaloneGetUser = useCallbackRef(authContext
|
|
17
|
+
? () => authContext.user || { id: '', email: '', displayName: '', roles: [] }
|
|
18
|
+
: undefined);
|
|
19
|
+
return useMemo(() => {
|
|
20
|
+
if (isShellMode && api) {
|
|
21
|
+
return {
|
|
22
|
+
getAccessToken: api.auth.getAccessToken,
|
|
23
|
+
hasPermission: api.auth.hasPermission,
|
|
24
|
+
getUser: api.auth.getUser,
|
|
25
|
+
logout: api.auth.logout,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (authContext) {
|
|
29
|
+
return {
|
|
30
|
+
getAccessToken: authContext.getAccessToken,
|
|
31
|
+
hasPermission: authContext.hasPermission,
|
|
32
|
+
getUser: standaloneGetUser,
|
|
33
|
+
logout: authContext.logout,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
throw new Error('useAuth must be used within AuthProvider. ' +
|
|
37
|
+
'Wrap your app with <AuthProvider authClient={...}> from @nsxbet/admin-sdk');
|
|
38
|
+
}, [isShellMode, api, authContext, standaloneGetUser]);
|
|
34
39
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a stable function reference that always invokes the latest callback.
|
|
3
|
+
*
|
|
4
|
+
* Follows the Radix UI / "Latest Ref" pattern: the returned function identity
|
|
5
|
+
* never changes, but calling it always executes the most recent `callback`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useCallbackRef<T extends (...args: any[]) => any>(callback: T | undefined): T;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef, useEffect, useMemo } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a stable function reference that always invokes the latest callback.
|
|
4
|
+
*
|
|
5
|
+
* Follows the Radix UI / "Latest Ref" pattern: the returned function identity
|
|
6
|
+
* never changes, but calling it always executes the most recent `callback`.
|
|
7
|
+
*/
|
|
8
|
+
export function useCallbackRef(callback) {
|
|
9
|
+
const callbackRef = useRef(callback);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
callbackRef.current = callback;
|
|
12
|
+
});
|
|
13
|
+
return useMemo(() => ((...args) => callbackRef.current?.(...args)), []);
|
|
14
|
+
}
|
package/dist/hooks/useFetch.js
CHANGED
|
@@ -18,8 +18,13 @@ export function useFetch() {
|
|
|
18
18
|
// In shell mode, use platform fetch (handles auth automatically)
|
|
19
19
|
return api.fetch(input, init);
|
|
20
20
|
}
|
|
21
|
-
// In standalone mode, add auth header manually
|
|
22
21
|
const token = await getAccessToken();
|
|
22
|
+
if (token === null) {
|
|
23
|
+
return fetch(input, {
|
|
24
|
+
...init,
|
|
25
|
+
credentials: 'include',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
23
28
|
return fetch(input, {
|
|
24
29
|
...init,
|
|
25
30
|
headers: {
|
package/dist/hooks/useI18n.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { useEffect, useState, useCallback } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { usePlatformAPI } from './usePlatformAPI';
|
|
7
|
-
import { saveLocale, isSupportedLocale, i18n } from '../i18n';
|
|
7
|
+
import { saveLocale, isSupportedLocale, i18n, DEFAULT_LOCALE } from '../i18n';
|
|
8
8
|
/**
|
|
9
9
|
* useI18n provides internationalization methods and translation access
|
|
10
10
|
*
|
|
@@ -37,7 +37,7 @@ import { saveLocale, isSupportedLocale, i18n } from '../i18n';
|
|
|
37
37
|
*/
|
|
38
38
|
export function useI18n(namespace) {
|
|
39
39
|
const { isShellMode, api } = usePlatformAPI();
|
|
40
|
-
const [standaloneLocale, setStandaloneLocale] = useState(i18n.language ||
|
|
40
|
+
const [standaloneLocale, setStandaloneLocale] = useState(i18n.language || DEFAULT_LOCALE);
|
|
41
41
|
// Use react-i18next's useTranslation for reactive updates
|
|
42
42
|
const { t, i18n: i18nInstance } = useTranslation(namespace || 'shell');
|
|
43
43
|
// Subscribe to platform locale changes in shell mode
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to access the platform API
|
|
3
|
+
*/
|
|
4
|
+
import { useMemo } from 'react';
|
|
1
5
|
/**
|
|
2
6
|
* Returns the platform API and whether the module is running in shell mode
|
|
3
7
|
*/
|
|
4
8
|
export function usePlatformAPI() {
|
|
5
9
|
const isShellMode = typeof window !== 'undefined' && window.__ADMIN_PLATFORM_API__ !== undefined;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
api: isShellMode ? window.__ADMIN_PLATFORM_API__ : undefined,
|
|
9
|
-
};
|
|
10
|
+
const api = isShellMode ? window.__ADMIN_PLATFORM_API__ : undefined;
|
|
11
|
+
return useMemo(() => ({ isShellMode, api }), [isShellMode, api]);
|
|
10
12
|
}
|
package/dist/i18n/config.d.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Supports: Portuguese (Brazil), Spanish, English (US), Romanian
|
|
5
5
|
*/
|
|
6
6
|
import i18n from 'i18next';
|
|
7
|
-
export declare const SUPPORTED_LOCALES: readonly ["
|
|
7
|
+
export declare const SUPPORTED_LOCALES: readonly ["en-US", "pt-BR", "es", "ro"];
|
|
8
8
|
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
9
|
+
export declare const DEFAULT_LOCALE: SupportedLocale;
|
|
9
10
|
/**
|
|
10
11
|
* Localized string value - one string per supported locale.
|
|
11
12
|
* Used for manifest title, description, and command titles.
|
package/dist/i18n/config.js
CHANGED
|
@@ -9,7 +9,8 @@ import ptBR from './locales/pt-BR.json';
|
|
|
9
9
|
import es from './locales/es.json';
|
|
10
10
|
import enUS from './locales/en-US.json';
|
|
11
11
|
import ro from './locales/ro.json';
|
|
12
|
-
export const SUPPORTED_LOCALES = ['
|
|
12
|
+
export const SUPPORTED_LOCALES = ['en-US', 'pt-BR', 'es', 'ro'];
|
|
13
|
+
export const DEFAULT_LOCALE = 'en-US';
|
|
13
14
|
/**
|
|
14
15
|
* Resolves a localized string for the given locale.
|
|
15
16
|
* Falls back to en-US if the locale is missing.
|
|
@@ -69,12 +70,12 @@ export function registerModuleTranslations(namespace, translations) {
|
|
|
69
70
|
*/
|
|
70
71
|
function getSavedLocale() {
|
|
71
72
|
if (typeof window === 'undefined')
|
|
72
|
-
return
|
|
73
|
+
return DEFAULT_LOCALE;
|
|
73
74
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
74
75
|
if (saved && SUPPORTED_LOCALES.includes(saved)) {
|
|
75
76
|
return saved;
|
|
76
77
|
}
|
|
77
|
-
return
|
|
78
|
+
return DEFAULT_LOCALE;
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* Save locale to localStorage
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* i18n module exports
|
|
3
3
|
*/
|
|
4
|
-
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES, } from './config';
|
|
4
|
+
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, DEFAULT_LOCALE, LOCALE_FLAGS, LOCALE_NAMES, } from './config';
|
|
5
5
|
export type { SupportedLocale, LocalizedField } from './config';
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* i18n module exports
|
|
3
3
|
*/
|
|
4
|
-
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES, } from './config';
|
|
4
|
+
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, DEFAULT_LOCALE, LOCALE_FLAGS, LOCALE_NAMES, } from './config';
|
|
@@ -212,6 +212,13 @@
|
|
|
212
212
|
"baseUrl": "Base URL",
|
|
213
213
|
"id": "ID"
|
|
214
214
|
},
|
|
215
|
+
"loginPage": {
|
|
216
|
+
"welcomeTo": "Welcome to",
|
|
217
|
+
"brandName": "NSXBet Admin",
|
|
218
|
+
"subtitle": "Administration Platform",
|
|
219
|
+
"loginButton": "Login with Corporate SSO",
|
|
220
|
+
"secureAuth": "Secure Authentication"
|
|
221
|
+
},
|
|
215
222
|
"breadcrumbs": {
|
|
216
223
|
"home": "Home",
|
|
217
224
|
"registry": "Registry",
|
|
@@ -212,6 +212,13 @@
|
|
|
212
212
|
"baseUrl": "URL Base",
|
|
213
213
|
"id": "ID"
|
|
214
214
|
},
|
|
215
|
+
"loginPage": {
|
|
216
|
+
"welcomeTo": "Bienvenido a",
|
|
217
|
+
"brandName": "NSXBet Admin",
|
|
218
|
+
"subtitle": "Plataforma de Administración",
|
|
219
|
+
"loginButton": "Iniciar sesión con SSO Corporativo",
|
|
220
|
+
"secureAuth": "Autenticación Segura"
|
|
221
|
+
},
|
|
215
222
|
"breadcrumbs": {
|
|
216
223
|
"home": "Inicio",
|
|
217
224
|
"registry": "Registro",
|
|
@@ -212,6 +212,13 @@
|
|
|
212
212
|
"baseUrl": "URL Base",
|
|
213
213
|
"id": "ID"
|
|
214
214
|
},
|
|
215
|
+
"loginPage": {
|
|
216
|
+
"welcomeTo": "Bem-vindo ao",
|
|
217
|
+
"brandName": "NSXBet Admin",
|
|
218
|
+
"subtitle": "Plataforma de Administração",
|
|
219
|
+
"loginButton": "Entrar com SSO Corporativo",
|
|
220
|
+
"secureAuth": "Autenticação Segura"
|
|
221
|
+
},
|
|
215
222
|
"breadcrumbs": {
|
|
216
223
|
"home": "Início",
|
|
217
224
|
"registry": "Registro",
|
|
@@ -212,6 +212,13 @@
|
|
|
212
212
|
"baseUrl": "URL Bază",
|
|
213
213
|
"id": "ID"
|
|
214
214
|
},
|
|
215
|
+
"loginPage": {
|
|
216
|
+
"welcomeTo": "Bine ai venit la",
|
|
217
|
+
"brandName": "NSXBet Admin",
|
|
218
|
+
"subtitle": "Platformă de Administrare",
|
|
219
|
+
"loginButton": "Autentificare cu SSO Corporativ",
|
|
220
|
+
"secureAuth": "Autentificare Securizată"
|
|
221
|
+
},
|
|
215
222
|
"breadcrumbs": {
|
|
216
223
|
"home": "Acasă",
|
|
217
224
|
"registry": "Registru",
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* SDK for building NSX Admin modules
|
|
5
5
|
* Provides authentication, navigation, and platform integration
|
|
6
6
|
*/
|
|
7
|
+
export { env, type EnvConfig } from "./env";
|
|
8
|
+
export { SDK_PACKAGE_VERSION } from "./sdk-version";
|
|
7
9
|
export { AuthProvider, useAuthContext, useOptionalAuthContext } from './components/AuthProvider';
|
|
8
10
|
export { Timestamp } from './components/Timestamp';
|
|
9
11
|
export type { TimestampProps } from './components/Timestamp';
|
|
@@ -13,19 +15,19 @@ export { useFetch } from './hooks/useFetch';
|
|
|
13
15
|
export { useTelemetry } from './hooks/useTelemetry';
|
|
14
16
|
export { useI18n } from './hooks/useI18n';
|
|
15
17
|
export { useTimestamp } from './hooks/useTimestamp';
|
|
16
|
-
export {
|
|
17
|
-
export
|
|
18
|
+
export { useCallbackRef } from './hooks/useCallbackRef';
|
|
19
|
+
export { createInMemoryAuthClient, clearInMemoryAuth, createMockUsersFromRoles, createBffAuthClient, clearLoggedOutFlag, isLoggedOutFlag, setLoggedOutFlag, UserSelector, LoginPage, } from './auth';
|
|
20
|
+
export type { AuthClient, InMemoryAuthClient, MockUser, MockUserRoles, AuthState, AuthStateCallback, InMemoryAuthClientOptions, BffAuthClient, BffAuthClientOptions, BffMeResponse, LoginPageProps, } from './auth';
|
|
18
21
|
export type { PlatformAPI, Breadcrumb, User, TimezoneMode, TimestampFormat } from './types/platform';
|
|
19
|
-
export type { KeycloakConfig } from './types/keycloak';
|
|
20
22
|
export type { UseAuthResult } from './hooks/useAuth';
|
|
21
23
|
export type { UseTelemetryResult } from './hooks/useTelemetry';
|
|
22
24
|
export type { UseI18nResult } from './hooks/useI18n';
|
|
23
25
|
export type { UseTimestampResult } from './hooks/useTimestamp';
|
|
24
|
-
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES, } from './i18n';
|
|
26
|
+
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, DEFAULT_LOCALE, LOCALE_FLAGS, LOCALE_NAMES, } from './i18n';
|
|
25
27
|
export type { SupportedLocale, LocalizedField } from './i18n';
|
|
26
28
|
export { createInMemoryRegistryClient, clearInMemoryRegistry, createHttpRegistryClient, validateManifest, parseManifest, useRegistryPolling, AdminShellRegistry, useRegistry, useRegistryClient, } from './registry';
|
|
27
29
|
export type { RegistryClient, ModuleOperations, CatalogOperations, InMemoryRegistryOptions, PreConfiguredModule, HttpRegistryOptions, AdminModuleManifest, ModuleCommand as RegistryModuleCommand, ModulePermissions, ModuleOwners, RegisteredModule, UpdateModuleDto, ReorderModuleDto, ModuleFilters, Catalog as RegistryCatalog, CatalogModule, CatalogVersion, ImportMap as RegistryImportMap, RegistryPollingState, UseRegistryPollingOptions, RegistryContextValue, AdminShellRegistryProps, } from './registry';
|
|
28
30
|
export { AdminShell, TopBar, LeftNav, MainContent, CommandPalette, ThemeProvider, useTheme, initTelemetry, track, trackError, fuzzySearch, } from './shell';
|
|
29
31
|
export { DynamicModule, ModuleLoading, ModuleErrorBoundary, } from './router';
|
|
30
32
|
export type { ModuleInfo, ErrorBoundaryProps, } from './router';
|
|
31
|
-
export type { AdminShellProps, TopBarProps, CommandPaletteProps, Module, ModuleCommand, ModuleStatus, NavigationSection, ModuleNavigation, Catalog, ImportMap, SearchableItem, SearchResult, SearchOptions, } from './shell';
|
|
33
|
+
export type { AdminShellProps, AdminShellBffConfig, TopBarProps, CommandPaletteProps, Module, ModuleCommand, ModuleStatus, NavigationSection, ModuleNavigation, Catalog, ImportMap, SearchableItem, SearchResult, SearchOptions, } from './shell';
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* SDK for building NSX Admin modules
|
|
5
5
|
* Provides authentication, navigation, and platform integration
|
|
6
6
|
*/
|
|
7
|
+
// Runtime env (`window.__ENV__` / `/env.js`)
|
|
8
|
+
export { env } from "./env";
|
|
9
|
+
export { SDK_PACKAGE_VERSION } from "./sdk-version";
|
|
7
10
|
// Components
|
|
8
11
|
export { AuthProvider, useAuthContext, useOptionalAuthContext } from './components/AuthProvider';
|
|
9
12
|
export { Timestamp } from './components/Timestamp';
|
|
@@ -14,10 +17,11 @@ export { useFetch } from './hooks/useFetch';
|
|
|
14
17
|
export { useTelemetry } from './hooks/useTelemetry';
|
|
15
18
|
export { useI18n } from './hooks/useI18n';
|
|
16
19
|
export { useTimestamp } from './hooks/useTimestamp';
|
|
20
|
+
export { useCallbackRef } from './hooks/useCallbackRef';
|
|
17
21
|
// Auth Client
|
|
18
|
-
export { createInMemoryAuthClient, clearInMemoryAuth, createMockUsersFromRoles,
|
|
22
|
+
export { createInMemoryAuthClient, clearInMemoryAuth, createMockUsersFromRoles, createBffAuthClient, clearLoggedOutFlag, isLoggedOutFlag, setLoggedOutFlag, UserSelector, LoginPage, } from './auth';
|
|
19
23
|
// i18n
|
|
20
|
-
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES, } from './i18n';
|
|
24
|
+
export { initI18n, saveLocale, isSupportedLocale, resolveLocalizedString, registerModuleTranslations, i18n, SUPPORTED_LOCALES, DEFAULT_LOCALE, LOCALE_FLAGS, LOCALE_NAMES, } from './i18n';
|
|
21
25
|
// Registry
|
|
22
26
|
export {
|
|
23
27
|
// Client factory
|
|
@@ -26,6 +26,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
import { createContext, useContext, useState, useEffect, useCallback, useMemo, } from 'react';
|
|
29
|
+
import { useCallbackRef } from '../hooks/useCallbackRef';
|
|
29
30
|
const RegistryContext = createContext(null);
|
|
30
31
|
/**
|
|
31
32
|
* Registry provider component
|
|
@@ -51,14 +52,14 @@ export function AdminShellRegistry({ store, children, onModulesChange, }) {
|
|
|
51
52
|
const [modules, setModules] = useState([]);
|
|
52
53
|
const [isLoading, setIsLoading] = useState(true);
|
|
53
54
|
const [error, setError] = useState(null);
|
|
54
|
-
|
|
55
|
+
const stableOnModulesChange = useCallbackRef(onModulesChange);
|
|
55
56
|
const reload = useCallback(async () => {
|
|
56
57
|
setIsLoading(true);
|
|
57
58
|
setError(null);
|
|
58
59
|
try {
|
|
59
60
|
const result = await store.modules.findAll();
|
|
60
61
|
setModules(result);
|
|
61
|
-
|
|
62
|
+
stableOnModulesChange(result);
|
|
62
63
|
}
|
|
63
64
|
catch (err) {
|
|
64
65
|
setError(err instanceof Error ? err : new Error('Failed to load modules'));
|
|
@@ -67,7 +68,7 @@ export function AdminShellRegistry({ store, children, onModulesChange, }) {
|
|
|
67
68
|
finally {
|
|
68
69
|
setIsLoading(false);
|
|
69
70
|
}
|
|
70
|
-
}, [store,
|
|
71
|
+
}, [store, stableOnModulesChange]);
|
|
71
72
|
// Initial load
|
|
72
73
|
useEffect(() => {
|
|
73
74
|
reload();
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Implementation of RegistryClient that communicates with the admin-api.
|
|
5
5
|
* Used when running with MySQL backend.
|
|
6
6
|
*/
|
|
7
|
+
import { SDK_PACKAGE_VERSION } from '../../sdk-version.js';
|
|
7
8
|
/**
|
|
8
9
|
* Create an HTTP registry client
|
|
9
10
|
*/
|
|
@@ -61,9 +62,13 @@ export function createHttpRegistryClient(options) {
|
|
|
61
62
|
});
|
|
62
63
|
},
|
|
63
64
|
async registerFromManifest(manifest, baseUrl = '') {
|
|
65
|
+
const merged = {
|
|
66
|
+
...manifest,
|
|
67
|
+
sdkVersion: manifest.sdkVersion ?? SDK_PACKAGE_VERSION,
|
|
68
|
+
};
|
|
64
69
|
return request('/modules/manifest', {
|
|
65
70
|
method: 'POST',
|
|
66
|
-
body: JSON.stringify({ manifest, baseUrl }),
|
|
71
|
+
body: JSON.stringify({ manifest: merged, baseUrl }),
|
|
67
72
|
});
|
|
68
73
|
},
|
|
69
74
|
async update(id, data) {
|
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
* Used for local development and testing without a backend.
|
|
6
6
|
*/
|
|
7
7
|
import { parseManifest } from '../types';
|
|
8
|
+
import { SDK_PACKAGE_VERSION } from '../../sdk-version.js';
|
|
9
|
+
function parseManifestWithSdkDefault(value) {
|
|
10
|
+
const raw = value;
|
|
11
|
+
const merged = {
|
|
12
|
+
...raw,
|
|
13
|
+
sdkVersion: typeof raw.sdkVersion === 'string' && raw.sdkVersion.trim() !== ''
|
|
14
|
+
? raw.sdkVersion
|
|
15
|
+
: SDK_PACKAGE_VERSION,
|
|
16
|
+
};
|
|
17
|
+
return parseManifest(merged);
|
|
18
|
+
}
|
|
8
19
|
const DEFAULT_STORAGE_KEY = '@nsxbet/registry';
|
|
9
20
|
/**
|
|
10
21
|
* Create an in-memory registry client
|
|
@@ -47,12 +58,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
47
58
|
if (options.seed && data.modules.length === 0) {
|
|
48
59
|
const now = new Date().toISOString();
|
|
49
60
|
for (const manifest of options.seed) {
|
|
50
|
-
const validated =
|
|
61
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
51
62
|
data.modules.push({
|
|
52
63
|
id: ++data.autoIncrement,
|
|
53
64
|
moduleId: validated.id,
|
|
54
65
|
baseUrl: '',
|
|
55
66
|
manifest: validated,
|
|
67
|
+
sdkVersion: validated.sdkVersion,
|
|
56
68
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
57
69
|
enabled: true,
|
|
58
70
|
registeredAt: now,
|
|
@@ -66,12 +78,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
66
78
|
const now = new Date().toISOString();
|
|
67
79
|
for (const moduleConfig of options.initialModules) {
|
|
68
80
|
const { baseUrl, ...manifestData } = moduleConfig;
|
|
69
|
-
const validated =
|
|
81
|
+
const validated = parseManifestWithSdkDefault(manifestData);
|
|
70
82
|
data.modules.push({
|
|
71
83
|
id: ++data.autoIncrement,
|
|
72
84
|
moduleId: validated.id,
|
|
73
85
|
baseUrl,
|
|
74
86
|
manifest: validated,
|
|
87
|
+
sdkVersion: validated.sdkVersion,
|
|
75
88
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
76
89
|
enabled: true,
|
|
77
90
|
registeredAt: now,
|
|
@@ -92,7 +105,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
92
105
|
result = result.filter((m) => m.manifest.category === filters.category);
|
|
93
106
|
}
|
|
94
107
|
if (filters?.status) {
|
|
95
|
-
result = result.filter(() => filters.status === 'active');
|
|
108
|
+
result = result.filter((m) => filters.status === 'active' ? m.enabled : !m.enabled);
|
|
96
109
|
}
|
|
97
110
|
// Sort by navigationOrder
|
|
98
111
|
result.sort((a, b) => a.navigationOrder - b.navigationOrder);
|
|
@@ -119,13 +132,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
119
132
|
const manifestData = await response.json();
|
|
120
133
|
// Extract manifest without entry (entry is fetched by DynamicModule at runtime)
|
|
121
134
|
const { entry: _entry, ...manifest } = manifestData;
|
|
122
|
-
const validated =
|
|
135
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
123
136
|
return this.registerFromManifest(validated, baseUrl);
|
|
124
137
|
},
|
|
125
138
|
async registerFromManifest(manifest, baseUrl = '') {
|
|
126
139
|
data = loadStorage(storageKey);
|
|
127
140
|
// Validate manifest
|
|
128
|
-
const validated =
|
|
141
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
129
142
|
// Check for duplicate moduleId
|
|
130
143
|
if (data.modules.some((m) => m.moduleId === validated.id)) {
|
|
131
144
|
throw new Error(`Module with id ${validated.id} already exists`);
|
|
@@ -136,6 +149,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
136
149
|
moduleId: validated.id,
|
|
137
150
|
baseUrl,
|
|
138
151
|
manifest: validated,
|
|
152
|
+
sdkVersion: validated.sdkVersion,
|
|
139
153
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
140
154
|
enabled: true,
|
|
141
155
|
registeredAt: now,
|
|
@@ -265,6 +279,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
265
279
|
supportChannel: m.manifest.owners?.supportChannel ?? '#general',
|
|
266
280
|
},
|
|
267
281
|
status: 'active',
|
|
282
|
+
sdkVersion: m.manifest.sdkVersion ?? SDK_PACKAGE_VERSION,
|
|
268
283
|
navigationOrder: m.navigationOrder,
|
|
269
284
|
icon: m.manifest.icon,
|
|
270
285
|
navigation: m.manifest.navigation ? {
|
|
@@ -82,6 +82,11 @@ export interface AdminModuleManifest {
|
|
|
82
82
|
permissions?: ModulePermissions;
|
|
83
83
|
/** Module ownership info */
|
|
84
84
|
owners?: ModuleOwners;
|
|
85
|
+
/**
|
|
86
|
+
* Semver of @nsxbet/admin-sdk the module was built against.
|
|
87
|
+
* Injected at build time by `defineModuleConfig` — omit in source `admin.module.json`.
|
|
88
|
+
*/
|
|
89
|
+
sdkVersion?: string;
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
87
92
|
* Validates that an object is a valid AdminModuleManifest
|
|
@@ -36,6 +36,9 @@ export function validateManifest(value) {
|
|
|
36
36
|
if (!isLocalizedField(obj.description)) {
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
+
if (typeof obj.sdkVersion !== "string" || obj.sdkVersion.trim() === "") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
39
42
|
// Optional field validations
|
|
40
43
|
if (obj.category !== undefined && typeof obj.category !== 'string') {
|
|
41
44
|
return false;
|
|
@@ -74,7 +77,7 @@ export function validateManifest(value) {
|
|
|
74
77
|
*/
|
|
75
78
|
export function parseManifest(value) {
|
|
76
79
|
if (!validateManifest(value)) {
|
|
77
|
-
throw new Error(
|
|
80
|
+
throw new Error("Invalid manifest: must include id, title (all locales), description (all locales), routeBase (starting with /), and sdkVersion (non-empty string, injected at build time by defineModuleConfig). Title and description must include en-US, pt-BR, es, ro.");
|
|
78
81
|
}
|
|
79
82
|
return value;
|
|
80
83
|
}
|
|
@@ -18,6 +18,8 @@ export interface RegisteredModule {
|
|
|
18
18
|
version?: string;
|
|
19
19
|
/** Full parsed module.manifest.json (without entry - fetched at runtime) */
|
|
20
20
|
manifest: AdminModuleManifest;
|
|
21
|
+
/** Semver of @nsxbet/admin-sdk (DB column; mirrors manifest.sdkVersion) */
|
|
22
|
+
sdkVersion?: string;
|
|
21
23
|
/** Admin-controlled navigation order */
|
|
22
24
|
navigationOrder: number;
|
|
23
25
|
/** Whether the module is enabled */
|
|
@@ -59,7 +61,7 @@ export interface ModuleFilters {
|
|
|
59
61
|
/** Filter by category */
|
|
60
62
|
category?: string;
|
|
61
63
|
/** Filter by status */
|
|
62
|
-
status?: 'active' | '
|
|
64
|
+
status?: 'active' | 'disabled';
|
|
63
65
|
}
|
|
64
66
|
/**
|
|
65
67
|
* Catalog structure for shell consumption (denormalized)
|
|
@@ -96,7 +98,9 @@ export interface CatalogModule {
|
|
|
96
98
|
team: string;
|
|
97
99
|
supportChannel: string;
|
|
98
100
|
};
|
|
99
|
-
status: 'active' | '
|
|
101
|
+
status: 'active' | 'disabled';
|
|
102
|
+
/** Semver of @nsxbet/admin-sdk the module was built against */
|
|
103
|
+
sdkVersion: string;
|
|
100
104
|
navigationOrder?: number;
|
|
101
105
|
icon?: string;
|
|
102
106
|
navigation?: {
|