@nsxbet/admin-sdk 0.5.1 → 0.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/CHECKLIST.md +52 -10
- package/README.md +371 -36
- package/dist/auth/client/gateway-token.d.ts +19 -0
- package/dist/auth/client/gateway-token.js +89 -0
- package/dist/auth/client/in-memory.d.ts +5 -1
- package/dist/auth/client/in-memory.js +75 -38
- package/dist/auth/client/index.d.ts +0 -1
- package/dist/auth/client/interface.d.ts +6 -3
- package/dist/auth/client/keycloak.d.ts +0 -1
- package/dist/auth/client/keycloak.js +6 -3
- package/dist/auth/components/UserSelector.d.ts +0 -1
- package/dist/auth/components/UserSelector.js +89 -7
- package/dist/auth/components/index.d.ts +0 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/components/AuthProvider.d.ts +0 -1
- package/dist/components/Timestamp.d.ts +7 -0
- package/dist/components/Timestamp.js +50 -0
- package/dist/hooks/useAuth.d.ts +0 -1
- package/dist/hooks/useAuth.js +1 -1
- package/dist/hooks/useFetch.d.ts +0 -1
- package/dist/hooks/useI18n.d.ts +0 -1
- package/dist/hooks/usePlatformAPI.d.ts +0 -1
- package/dist/hooks/useTelemetry.d.ts +0 -1
- package/dist/hooks/useTimestamp.d.ts +8 -0
- package/dist/hooks/useTimestamp.js +122 -0
- package/dist/i18n/config.d.ts +20 -2
- package/dist/i18n/config.js +48 -0
- package/dist/i18n/index.d.ts +2 -3
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/locales/en-US.json +95 -18
- package/dist/i18n/locales/es.json +95 -18
- package/dist/i18n/locales/pt-BR.json +95 -18
- package/dist/i18n/locales/ro.json +95 -18
- package/dist/index.d.ts +11 -7
- package/dist/index.js +5 -1
- package/dist/registry/AdminShellRegistry.d.ts +1 -2
- package/dist/registry/cache/cached-catalog.d.ts +11 -0
- package/dist/registry/cache/cached-catalog.js +42 -0
- package/dist/registry/cache/catalog-cache.d.ts +10 -0
- package/dist/registry/cache/catalog-cache.js +58 -0
- package/dist/registry/cache/index.d.ts +5 -0
- package/dist/registry/cache/index.js +3 -0
- package/dist/registry/cache/types.d.ts +20 -0
- package/dist/registry/cache/types.js +3 -0
- package/dist/registry/client/http.d.ts +0 -1
- package/dist/registry/client/http.js +13 -0
- package/dist/registry/client/in-memory.d.ts +0 -1
- package/dist/registry/client/in-memory.js +117 -12
- package/dist/registry/client/index.d.ts +0 -1
- package/dist/registry/client/interface.d.ts +21 -6
- package/dist/registry/index.d.ts +5 -2
- package/dist/registry/index.js +4 -0
- package/dist/registry/types/index.d.ts +2 -3
- package/dist/registry/types/manifest.d.ts +20 -24
- package/dist/registry/types/manifest.js +17 -18
- package/dist/registry/types/module.d.ts +43 -14
- package/dist/registry/useRegistryPolling.d.ts +15 -0
- package/dist/registry/useRegistryPolling.js +66 -0
- package/dist/router/DynamicModule.d.ts +6 -22
- package/dist/router/DynamicModule.js +25 -48
- package/dist/router/ModuleErrorBoundary.d.ts +39 -0
- package/dist/router/ModuleErrorBoundary.js +101 -0
- package/dist/router/index.d.ts +1 -1
- package/dist/router/url-allowlist.d.ts +22 -0
- package/dist/router/url-allowlist.js +65 -0
- package/dist/shell/AdminShell.d.ts +0 -1
- package/dist/shell/AdminShell.js +178 -43
- package/dist/shell/BackofficeShell.d.ts +0 -1
- package/dist/shell/BackofficeShell.js +59 -25
- package/dist/shell/components/CommandPalette.d.ts +0 -1
- package/dist/shell/components/CommandPalette.js +26 -50
- package/dist/shell/components/DevtoolsPanel.d.ts +11 -0
- package/dist/shell/components/DevtoolsPanel.js +145 -0
- package/dist/shell/components/HomePage.d.ts +0 -1
- package/dist/shell/components/HomePage.js +9 -4
- package/dist/shell/components/LeftNav.d.ts +0 -1
- package/dist/shell/components/LeftNav.js +91 -93
- package/dist/shell/components/MainContent.d.ts +3 -2
- package/dist/shell/components/MainContent.js +8 -23
- package/dist/shell/components/ModuleOverview.d.ts +0 -1
- package/dist/shell/components/ModuleOverview.js +4 -20
- package/dist/shell/components/ProfilePage.d.ts +0 -1
- package/dist/shell/components/ProfilePage.js +1 -1
- package/dist/shell/components/RegistryPage.d.ts +0 -1
- package/dist/shell/components/RegistryPage.js +154 -64
- package/dist/shell/components/RegistryStatusBanner.d.ts +6 -0
- package/dist/shell/components/RegistryStatusBanner.js +31 -0
- package/dist/shell/components/RegistryUnavailable.d.ts +4 -0
- package/dist/shell/components/RegistryUnavailable.js +7 -0
- package/dist/shell/components/SettingsPage.d.ts +0 -1
- package/dist/shell/components/StackedPanel.d.ts +15 -0
- package/dist/shell/components/StackedPanel.js +45 -0
- package/dist/shell/components/TopBar.d.ts +4 -2
- package/dist/shell/components/TopBar.js +9 -3
- package/dist/shell/components/UpdateBanner.d.ts +5 -0
- package/dist/shell/components/UpdateBanner.js +8 -0
- package/dist/shell/components/index.d.ts +4 -1
- package/dist/shell/components/index.js +2 -0
- package/dist/shell/components/theme-provider.d.ts +0 -1
- package/dist/shell/components/theme-provider.js +8 -5
- package/dist/shell/hooks/useCspViolations.d.ts +12 -0
- package/dist/shell/hooks/useCspViolations.js +34 -0
- package/dist/shell/index.d.ts +1 -2
- package/dist/shell/polling-config.d.ts +10 -0
- package/dist/shell/polling-config.js +26 -0
- package/dist/shell/search/fuzzy.d.ts +0 -1
- package/dist/shell/search/index.d.ts +0 -1
- package/dist/shell/telemetry.d.ts +0 -1
- package/dist/shell/types.d.ts +34 -18
- package/dist/tailwind/index.d.ts +0 -1
- package/dist/types/keycloak.d.ts +0 -1
- package/dist/types/platform.d.ts +12 -1
- package/dist/vite/AdminShellSharedDeps.d.ts +64 -0
- package/dist/vite/AdminShellSharedDeps.js +215 -0
- package/dist/vite/config.d.ts +17 -3
- package/dist/vite/config.js +34 -8
- package/dist/vite/i18n-plugin.d.ts +13 -0
- package/dist/vite/i18n-plugin.js +81 -0
- package/dist/vite/index.d.ts +3 -2
- package/dist/vite/index.js +3 -1
- package/dist/vite/plugins.d.ts +0 -1
- package/package.json +6 -2
- package/dist/auth/client/in-memory.d.ts.map +0 -1
- package/dist/auth/client/index.d.ts.map +0 -1
- package/dist/auth/client/interface.d.ts.map +0 -1
- package/dist/auth/client/keycloak.d.ts.map +0 -1
- package/dist/auth/components/UserSelector.d.ts.map +0 -1
- package/dist/auth/components/index.d.ts.map +0 -1
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/components/AuthProvider.d.ts.map +0 -1
- package/dist/hooks/useAuth.d.ts.map +0 -1
- package/dist/hooks/useFetch.d.ts.map +0 -1
- package/dist/hooks/useI18n.d.ts.map +0 -1
- package/dist/hooks/usePlatformAPI.d.ts.map +0 -1
- package/dist/hooks/useTelemetry.d.ts.map +0 -1
- package/dist/i18n/config.d.ts.map +0 -1
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/registry/AdminShellRegistry.d.ts.map +0 -1
- package/dist/registry/client/http.d.ts.map +0 -1
- package/dist/registry/client/in-memory.d.ts.map +0 -1
- package/dist/registry/client/index.d.ts.map +0 -1
- package/dist/registry/client/interface.d.ts.map +0 -1
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/types/index.d.ts.map +0 -1
- package/dist/registry/types/manifest.d.ts.map +0 -1
- package/dist/registry/types/module.d.ts.map +0 -1
- package/dist/router/DynamicModule.d.ts.map +0 -1
- package/dist/router/index.d.ts.map +0 -1
- package/dist/shell/AdminShell.d.ts.map +0 -1
- package/dist/shell/BackofficeShell.d.ts.map +0 -1
- package/dist/shell/components/CommandPalette.d.ts.map +0 -1
- package/dist/shell/components/HomePage.d.ts.map +0 -1
- package/dist/shell/components/LeftNav.d.ts.map +0 -1
- package/dist/shell/components/MainContent.d.ts.map +0 -1
- package/dist/shell/components/ModuleOverview.d.ts.map +0 -1
- package/dist/shell/components/ProfilePage.d.ts.map +0 -1
- package/dist/shell/components/RegistryPage.d.ts.map +0 -1
- package/dist/shell/components/SettingsPage.d.ts.map +0 -1
- package/dist/shell/components/TopBar.d.ts.map +0 -1
- package/dist/shell/components/index.d.ts.map +0 -1
- package/dist/shell/components/theme-provider.d.ts.map +0 -1
- package/dist/shell/index.d.ts.map +0 -1
- package/dist/shell/search/fuzzy.d.ts.map +0 -1
- package/dist/shell/search/index.d.ts.map +0 -1
- package/dist/shell/telemetry.d.ts.map +0 -1
- package/dist/shell/types.d.ts.map +0 -1
- package/dist/tailwind/index.d.ts.map +0 -1
- package/dist/types/keycloak.d.ts.map +0 -1
- package/dist/types/platform.d.ts.map +0 -1
- package/dist/vite/config.d.ts.map +0 -1
- package/dist/vite/index.d.ts.map +0 -1
- package/dist/vite/plugins.d.ts.map +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
const MAX_VIOLATIONS = 50;
|
|
3
|
+
export function useCspViolations(environment) {
|
|
4
|
+
const [violations, setViolations] = useState([]);
|
|
5
|
+
const [unseenCount, setUnseenCount] = useState(0);
|
|
6
|
+
const isProduction = environment === "production";
|
|
7
|
+
const resetUnseenCount = useCallback(() => {
|
|
8
|
+
setUnseenCount(0);
|
|
9
|
+
}, []);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (isProduction)
|
|
12
|
+
return;
|
|
13
|
+
const handler = (event) => {
|
|
14
|
+
const violation = {
|
|
15
|
+
blockedURI: event.blockedURI,
|
|
16
|
+
violatedDirective: event.violatedDirective,
|
|
17
|
+
effectiveDirective: event.effectiveDirective,
|
|
18
|
+
documentURI: event.documentURI,
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
};
|
|
21
|
+
setViolations((prev) => [violation, ...prev].slice(0, MAX_VIOLATIONS));
|
|
22
|
+
setUnseenCount((prev) => prev + 1);
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener("securitypolicyviolation", handler);
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener("securitypolicyviolation", handler);
|
|
27
|
+
};
|
|
28
|
+
}, [isProduction]);
|
|
29
|
+
return {
|
|
30
|
+
violations: isProduction ? [] : violations,
|
|
31
|
+
unseenCount: isProduction ? 0 : unseenCount,
|
|
32
|
+
resetUnseenCount,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -2,8 +2,7 @@ export { AdminShell } from "./AdminShell";
|
|
|
2
2
|
export type { AdminShellProps } from "./AdminShell";
|
|
3
3
|
export { TopBar, LeftNav, MainContent, CommandPalette, ThemeProvider, useTheme, } from "./components";
|
|
4
4
|
export type { TopBarProps, CommandPaletteProps } from "./components";
|
|
5
|
-
export type { Module, ModuleCommand, ModuleStatus, Catalog, ImportMap, SearchableItem, SearchResult, } from "./types";
|
|
5
|
+
export type { Module, ModuleCommand, ModuleStatus, NavigationSection, ModuleNavigation, Catalog, ImportMap, SearchableItem, SearchResult, } from "./types";
|
|
6
6
|
export { initTelemetry, track, trackError } from "./telemetry";
|
|
7
7
|
export { fuzzySearch } from "./search/fuzzy";
|
|
8
8
|
export type { SearchOptions } from "./search/fuzzy";
|
|
9
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the registry polling interval in milliseconds.
|
|
3
|
+
*
|
|
4
|
+
* Priority:
|
|
5
|
+
* 1. Explicit `REGISTRY_POLL_INTERVAL` from `window.__ENV__` or `import.meta.env`
|
|
6
|
+
* 2. Environment-based default (local: 60s, staging: 5min, production: 15min)
|
|
7
|
+
*
|
|
8
|
+
* Returns 0 (disabled) when the explicit value is "0".
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolvePollingInterval(environment: string): number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const ENVIRONMENT_DEFAULTS = {
|
|
2
|
+
local: 60000,
|
|
3
|
+
development: 60000,
|
|
4
|
+
e2e: 300000,
|
|
5
|
+
staging: 300000,
|
|
6
|
+
production: 900000,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the registry polling interval in milliseconds.
|
|
10
|
+
*
|
|
11
|
+
* Priority:
|
|
12
|
+
* 1. Explicit `REGISTRY_POLL_INTERVAL` from `window.__ENV__` or `import.meta.env`
|
|
13
|
+
* 2. Environment-based default (local: 60s, staging: 5min, production: 15min)
|
|
14
|
+
*
|
|
15
|
+
* Returns 0 (disabled) when the explicit value is "0".
|
|
16
|
+
*/
|
|
17
|
+
export function resolvePollingInterval(environment) {
|
|
18
|
+
const raw = (typeof window !== 'undefined' && window.__ENV__?.REGISTRY_POLL_INTERVAL) ||
|
|
19
|
+
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_REGISTRY_POLL_INTERVAL);
|
|
20
|
+
if (raw !== undefined && raw !== null && raw !== '') {
|
|
21
|
+
const parsed = Number(raw);
|
|
22
|
+
if (!Number.isNaN(parsed))
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
return ENVIRONMENT_DEFAULTS[environment] ?? ENVIRONMENT_DEFAULTS.local;
|
|
26
|
+
}
|
|
@@ -4,4 +4,3 @@
|
|
|
4
4
|
export declare function initTelemetry(env: string): void;
|
|
5
5
|
export declare function track(event: string, props?: Record<string, unknown>): void;
|
|
6
6
|
export declare function trackError(err: unknown, props?: Record<string, unknown>): void;
|
|
7
|
-
//# sourceMappingURL=telemetry.d.ts.map
|
package/dist/shell/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shell Type Definitions
|
|
3
3
|
*/
|
|
4
|
+
import type { LocalizedField } from "../i18n/config.js";
|
|
4
5
|
/**
|
|
5
6
|
* Module status in the registry
|
|
6
7
|
*/
|
|
@@ -10,12 +11,31 @@ export type ModuleStatus = "active" | "deprecated" | "disabled";
|
|
|
10
11
|
*/
|
|
11
12
|
export interface ModuleCommand {
|
|
12
13
|
id: string;
|
|
13
|
-
title
|
|
14
|
-
|
|
15
|
-
titleKey?: string;
|
|
14
|
+
/** Human-readable title (localized per locale) */
|
|
15
|
+
title: LocalizedField;
|
|
16
16
|
keywords?: string[];
|
|
17
17
|
route: string;
|
|
18
18
|
icon?: string;
|
|
19
|
+
/** Section ID for grouping in stacked navigation */
|
|
20
|
+
section?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Section within a stacked navigation panel
|
|
24
|
+
*/
|
|
25
|
+
export interface NavigationSection {
|
|
26
|
+
/** Unique identifier for the section */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Human-readable label (localized per locale) */
|
|
29
|
+
label: LocalizedField;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Navigation configuration for a module
|
|
33
|
+
*/
|
|
34
|
+
export interface ModuleNavigation {
|
|
35
|
+
/** Navigation style: "collapsible" (default inline expand) or "stacked" (dedicated panel) */
|
|
36
|
+
style: "collapsible" | "stacked";
|
|
37
|
+
/** Sections for grouping commands in stacked mode */
|
|
38
|
+
sections?: NavigationSection[];
|
|
19
39
|
}
|
|
20
40
|
/**
|
|
21
41
|
* Module information as defined in the catalog
|
|
@@ -24,14 +44,10 @@ export interface ModuleCommand {
|
|
|
24
44
|
export interface Module {
|
|
25
45
|
/** Unique identifier for the module (e.g., "@admin/users") */
|
|
26
46
|
id: string;
|
|
27
|
-
/** Human-readable title */
|
|
28
|
-
title:
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
/** Description of what the module does */
|
|
32
|
-
description: string;
|
|
33
|
-
/** i18n key for translated description (optional) */
|
|
34
|
-
descriptionKey?: string;
|
|
47
|
+
/** Human-readable title (localized per locale) */
|
|
48
|
+
title: LocalizedField;
|
|
49
|
+
/** Description of what the module does (localized per locale) */
|
|
50
|
+
description: LocalizedField;
|
|
35
51
|
/** Category for navigation grouping */
|
|
36
52
|
category: string;
|
|
37
53
|
/** Base route where module is mounted (e.g., "/admin/users") */
|
|
@@ -53,9 +69,11 @@ export interface Module {
|
|
|
53
69
|
/** Module status */
|
|
54
70
|
status: ModuleStatus;
|
|
55
71
|
/** Optional navigation order */
|
|
56
|
-
|
|
72
|
+
navigationOrder?: number;
|
|
57
73
|
/** Optional icon name */
|
|
58
74
|
icon?: string;
|
|
75
|
+
/** Navigation configuration (stacked or collapsible) */
|
|
76
|
+
navigation?: ModuleNavigation;
|
|
59
77
|
/** Optional commands */
|
|
60
78
|
commands?: ModuleCommand[];
|
|
61
79
|
}
|
|
@@ -80,22 +98,21 @@ export interface ImportMap {
|
|
|
80
98
|
};
|
|
81
99
|
}
|
|
82
100
|
/**
|
|
83
|
-
* Searchable item for command palette
|
|
101
|
+
* Searchable item for command palette.
|
|
102
|
+
* Title and moduleTitle are resolved for the current locale at index time.
|
|
84
103
|
*/
|
|
85
104
|
export interface SearchableItem {
|
|
86
105
|
type: "module" | "command";
|
|
87
106
|
id: string;
|
|
107
|
+
/** Resolved title for current locale */
|
|
88
108
|
title: string;
|
|
89
|
-
/** i18n key for the title */
|
|
90
|
-
titleKey?: string;
|
|
91
109
|
category?: string;
|
|
92
110
|
keywords?: string[];
|
|
93
111
|
description?: string;
|
|
94
112
|
route: string;
|
|
95
113
|
moduleId?: string;
|
|
114
|
+
/** Resolved module title for current locale */
|
|
96
115
|
moduleTitle?: string;
|
|
97
|
-
/** i18n key for the module title */
|
|
98
|
-
moduleTitleKey?: string;
|
|
99
116
|
/** Icon name (Lucide icon in kebab-case) */
|
|
100
117
|
icon?: string;
|
|
101
118
|
}
|
|
@@ -107,4 +124,3 @@ export interface SearchResult {
|
|
|
107
124
|
score: number;
|
|
108
125
|
matchedFields: string[];
|
|
109
126
|
}
|
|
110
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/tailwind/index.d.ts
CHANGED
package/dist/types/keycloak.d.ts
CHANGED
package/dist/types/platform.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Platform API type definitions
|
|
3
3
|
* These types define the global API exposed by the admin shell
|
|
4
4
|
*/
|
|
5
|
+
export type TimezoneMode = "utc" | "local";
|
|
6
|
+
export type TimestampFormat = "datetime" | "date" | "time" | "relative";
|
|
5
7
|
/**
|
|
6
8
|
* Breadcrumb item for navigation
|
|
7
9
|
*/
|
|
@@ -16,6 +18,7 @@ export interface User {
|
|
|
16
18
|
id: string;
|
|
17
19
|
email: string;
|
|
18
20
|
displayName: string;
|
|
21
|
+
roles: string[];
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Platform API exposed by the admin shell at window.__ADMIN_PLATFORM_API__
|
|
@@ -59,6 +62,15 @@ export interface PlatformAPI {
|
|
|
59
62
|
/** Track an error */
|
|
60
63
|
trackError: (err: unknown, props?: Record<string, unknown>) => void;
|
|
61
64
|
};
|
|
65
|
+
/** Timestamp timezone preference */
|
|
66
|
+
timestamp: {
|
|
67
|
+
/** Current timezone mode */
|
|
68
|
+
mode: TimezoneMode;
|
|
69
|
+
/** Change the timezone mode */
|
|
70
|
+
setMode: (mode: TimezoneMode) => void;
|
|
71
|
+
/** Subscribe to mode changes, returns unsubscribe function */
|
|
72
|
+
onModeChange: (callback: (mode: TimezoneMode) => void) => () => void;
|
|
73
|
+
};
|
|
62
74
|
/** Authenticated fetch wrapper */
|
|
63
75
|
fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
64
76
|
}
|
|
@@ -80,4 +92,3 @@ declare global {
|
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
export {};
|
|
83
|
-
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for a shared dependency exposed to dynamically loaded modules.
|
|
4
|
+
*/
|
|
5
|
+
export interface SharedDepConfig {
|
|
6
|
+
/** npm package specifier (e.g., "react") */
|
|
7
|
+
specifier: string;
|
|
8
|
+
/** Name of the window global where the shell exposes this dependency */
|
|
9
|
+
globalName: string;
|
|
10
|
+
/** Use `import X from` (true) vs `import * as X from` (false/default) */
|
|
11
|
+
importDefault?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Additional specifiers that resolve to the same shim file.
|
|
14
|
+
* Their exports are merged into the shim alongside the main specifier.
|
|
15
|
+
* Useful for subpath exports like "react-dom/client".
|
|
16
|
+
*/
|
|
17
|
+
mergedSpecifiers?: {
|
|
18
|
+
specifier: string;
|
|
19
|
+
globalName: string;
|
|
20
|
+
importDefault?: boolean;
|
|
21
|
+
}[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Single source of truth for shared dependencies between shell and modules.
|
|
25
|
+
*
|
|
26
|
+
* - Module builds externalize these via SHARED_EXTERNALS.
|
|
27
|
+
* - The shell plugin auto-injects a globals script, import map, and shim files.
|
|
28
|
+
* - Adding a dep here is all that's needed — no manual shim files, import map
|
|
29
|
+
* entries, or window assignments.
|
|
30
|
+
*/
|
|
31
|
+
export declare const SHARED_DEPS_CONFIG: SharedDepConfig[];
|
|
32
|
+
/**
|
|
33
|
+
* Flat list of all specifiers that should be externalized in module builds.
|
|
34
|
+
* Derived from SHARED_DEPS_CONFIG.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getSharedExternals(): string[];
|
|
37
|
+
export type AdminShellSharedDeps = ReturnType<typeof AdminShellSharedDeps.create>;
|
|
38
|
+
/**
|
|
39
|
+
* Vite plugin for the admin shell that auto-manages shared dependency resolution.
|
|
40
|
+
*
|
|
41
|
+
* What it does:
|
|
42
|
+
* 1. **Globals injection** — prepends `window.React = ...` assignments into the
|
|
43
|
+
* shell's entry file via the `transform` hook. This runs through Vite's full
|
|
44
|
+
* pipeline so imports resolve to pre-bundled deps (bypassing the import map).
|
|
45
|
+
* 2. **Import map** — injects `<script type="importmap">` so dynamically loaded
|
|
46
|
+
* modules can resolve bare specifiers like `"react"` to shim files.
|
|
47
|
+
* 3. **Shim files** — auto-generated ES modules that re-export from window globals.
|
|
48
|
+
* (dev: served via middleware, build: emitted as assets)
|
|
49
|
+
*
|
|
50
|
+
* With this plugin, main.tsx needs no awareness of the shared deps mechanism.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // apps/admin-shell/vite.config.ts
|
|
55
|
+
* import { AdminShellSharedDeps } from "@nsxbet/admin-sdk/vite";
|
|
56
|
+
*
|
|
57
|
+
* export default defineConfig({
|
|
58
|
+
* plugins: [react(), AdminShellSharedDeps.create()],
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare const AdminShellSharedDeps: {
|
|
63
|
+
create(): Plugin;
|
|
64
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for shared dependencies between shell and modules.
|
|
4
|
+
*
|
|
5
|
+
* - Module builds externalize these via SHARED_EXTERNALS.
|
|
6
|
+
* - The shell plugin auto-injects a globals script, import map, and shim files.
|
|
7
|
+
* - Adding a dep here is all that's needed — no manual shim files, import map
|
|
8
|
+
* entries, or window assignments.
|
|
9
|
+
*/
|
|
10
|
+
export const SHARED_DEPS_CONFIG = [
|
|
11
|
+
{ specifier: "react", globalName: "React", importDefault: true },
|
|
12
|
+
{
|
|
13
|
+
specifier: "react-dom",
|
|
14
|
+
globalName: "ReactDOM",
|
|
15
|
+
importDefault: true,
|
|
16
|
+
mergedSpecifiers: [
|
|
17
|
+
{ specifier: "react-dom/client", globalName: "ReactDOMClient" },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{ specifier: "react-router-dom", globalName: "ReactRouterDOM" },
|
|
21
|
+
{ specifier: "i18next", globalName: "i18next" },
|
|
22
|
+
{ specifier: "react-i18next", globalName: "reactI18next" },
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Flat list of all specifiers that should be externalized in module builds.
|
|
26
|
+
* Derived from SHARED_DEPS_CONFIG.
|
|
27
|
+
*/
|
|
28
|
+
export function getSharedExternals() {
|
|
29
|
+
return SHARED_DEPS_CONFIG.flatMap((dep) => [
|
|
30
|
+
dep.specifier,
|
|
31
|
+
...(dep.mergedSpecifiers?.map((m) => m.specifier) ?? []),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
function buildGlobalsScript() {
|
|
35
|
+
const lines = [];
|
|
36
|
+
for (const dep of SHARED_DEPS_CONFIG) {
|
|
37
|
+
const binding = `__${dep.globalName}`;
|
|
38
|
+
lines.push(dep.importDefault
|
|
39
|
+
? `import ${binding} from "${dep.specifier}";`
|
|
40
|
+
: `import * as ${binding} from "${dep.specifier}";`);
|
|
41
|
+
lines.push(`window.${dep.globalName} = ${binding};`);
|
|
42
|
+
for (const merged of dep.mergedSpecifiers ?? []) {
|
|
43
|
+
const mBinding = `__${merged.globalName}`;
|
|
44
|
+
lines.push(merged.importDefault
|
|
45
|
+
? `import ${mBinding} from "${merged.specifier}";`
|
|
46
|
+
: `import * as ${mBinding} from "${merged.specifier}";`);
|
|
47
|
+
lines.push(`window.${merged.globalName} = ${mBinding};`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
const VALID_JS_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
53
|
+
function isValidExportName(name) {
|
|
54
|
+
return VALID_JS_IDENT.test(name) && name !== "default" && name !== "__esModule";
|
|
55
|
+
}
|
|
56
|
+
async function enumerateNamedExports(specifier) {
|
|
57
|
+
const mod = await import(specifier);
|
|
58
|
+
return Object.keys(mod).filter(isValidExportName);
|
|
59
|
+
}
|
|
60
|
+
async function enumerateDefaultExportKeys(specifier) {
|
|
61
|
+
const mod = await import(specifier);
|
|
62
|
+
if (mod.default && typeof mod.default === "object") {
|
|
63
|
+
return Object.keys(mod.default).filter(isValidExportName);
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
async function generateShimContent(dep) {
|
|
68
|
+
const mainExports = dep.importDefault
|
|
69
|
+
? await enumerateDefaultExportKeys(dep.specifier)
|
|
70
|
+
: await enumerateNamedExports(dep.specifier);
|
|
71
|
+
const lines = [
|
|
72
|
+
`const _mod = window.${dep.globalName};`,
|
|
73
|
+
`export default _mod.default ?? _mod;`,
|
|
74
|
+
];
|
|
75
|
+
if (mainExports.length > 0) {
|
|
76
|
+
lines.push(`export const { ${mainExports.join(", ")} } = _mod;`);
|
|
77
|
+
}
|
|
78
|
+
for (const merged of dep.mergedSpecifiers ?? []) {
|
|
79
|
+
const mergedExports = await enumerateNamedExports(merged.specifier);
|
|
80
|
+
const unique = mergedExports.filter((e) => !mainExports.includes(e));
|
|
81
|
+
if (unique.length > 0) {
|
|
82
|
+
lines.push(`const _${merged.globalName} = window.${merged.globalName};`);
|
|
83
|
+
for (const name of unique) {
|
|
84
|
+
lines.push(`export const ${name} = _${merged.globalName}?.${name};`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
async function generateAllShims() {
|
|
91
|
+
const shims = new Map();
|
|
92
|
+
for (const dep of SHARED_DEPS_CONFIG) {
|
|
93
|
+
shims.set(dep.specifier, await generateShimContent(dep));
|
|
94
|
+
}
|
|
95
|
+
return shims;
|
|
96
|
+
}
|
|
97
|
+
function contentHash(content) {
|
|
98
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
99
|
+
}
|
|
100
|
+
async function generateHashedShims() {
|
|
101
|
+
const raw = await generateAllShims();
|
|
102
|
+
const hashed = new Map();
|
|
103
|
+
for (const [specifier, content] of raw) {
|
|
104
|
+
const hash = contentHash(content);
|
|
105
|
+
hashed.set(specifier, { content, fileName: `shims/${specifier}-${hash}.js` });
|
|
106
|
+
}
|
|
107
|
+
return hashed;
|
|
108
|
+
}
|
|
109
|
+
function buildHashedImportMapEntries(hashed, base) {
|
|
110
|
+
const imports = {};
|
|
111
|
+
for (const dep of SHARED_DEPS_CONFIG) {
|
|
112
|
+
const shimInfo = hashed.get(dep.specifier);
|
|
113
|
+
imports[dep.specifier] = `${base}${shimInfo.fileName}`;
|
|
114
|
+
for (const merged of dep.mergedSpecifiers ?? []) {
|
|
115
|
+
imports[merged.specifier] = `${base}${shimInfo.fileName}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return imports;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Vite plugin for the admin shell that auto-manages shared dependency resolution.
|
|
122
|
+
*
|
|
123
|
+
* What it does:
|
|
124
|
+
* 1. **Globals injection** — prepends `window.React = ...` assignments into the
|
|
125
|
+
* shell's entry file via the `transform` hook. This runs through Vite's full
|
|
126
|
+
* pipeline so imports resolve to pre-bundled deps (bypassing the import map).
|
|
127
|
+
* 2. **Import map** — injects `<script type="importmap">` so dynamically loaded
|
|
128
|
+
* modules can resolve bare specifiers like `"react"` to shim files.
|
|
129
|
+
* 3. **Shim files** — auto-generated ES modules that re-export from window globals.
|
|
130
|
+
* (dev: served via middleware, build: emitted as assets)
|
|
131
|
+
*
|
|
132
|
+
* With this plugin, main.tsx needs no awareness of the shared deps mechanism.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* // apps/admin-shell/vite.config.ts
|
|
137
|
+
* import { AdminShellSharedDeps } from "@nsxbet/admin-sdk/vite";
|
|
138
|
+
*
|
|
139
|
+
* export default defineConfig({
|
|
140
|
+
* plugins: [react(), AdminShellSharedDeps.create()],
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export const AdminShellSharedDeps = {
|
|
145
|
+
create() {
|
|
146
|
+
let hashedShimsPromise = null;
|
|
147
|
+
let resolvedBase = "/";
|
|
148
|
+
let entryPattern;
|
|
149
|
+
function getHashedShims() {
|
|
150
|
+
if (!hashedShimsPromise) {
|
|
151
|
+
hashedShimsPromise = generateHashedShims();
|
|
152
|
+
}
|
|
153
|
+
return hashedShimsPromise;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
name: "admin-shell-shared-deps",
|
|
157
|
+
configResolved(config) {
|
|
158
|
+
resolvedBase = config.base;
|
|
159
|
+
entryPattern = config.build?.rollupOptions?.input;
|
|
160
|
+
},
|
|
161
|
+
transform(code, id) {
|
|
162
|
+
if (id.includes("node_modules"))
|
|
163
|
+
return;
|
|
164
|
+
const isEntry = entryPattern
|
|
165
|
+
? id.endsWith(entryPattern)
|
|
166
|
+
: /\/src\/main\.[tj]sx?$/.test(id);
|
|
167
|
+
if (!isEntry)
|
|
168
|
+
return;
|
|
169
|
+
return buildGlobalsScript() + "\n" + code;
|
|
170
|
+
},
|
|
171
|
+
configureServer(server) {
|
|
172
|
+
server.middlewares.use((req, res, next) => {
|
|
173
|
+
const match = req.url?.match(/^\/shims\/(.+?)(?:-[a-f0-9]+)?\.js$/);
|
|
174
|
+
if (!match)
|
|
175
|
+
return next();
|
|
176
|
+
const specifier = match[1];
|
|
177
|
+
getHashedShims()
|
|
178
|
+
.then((shims) => {
|
|
179
|
+
const shimInfo = shims.get(specifier);
|
|
180
|
+
if (shimInfo) {
|
|
181
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
182
|
+
res.end(shimInfo.content);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
next();
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
.catch(next);
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
async transformIndexHtml() {
|
|
192
|
+
const hashed = await getHashedShims();
|
|
193
|
+
const imports = buildHashedImportMapEntries(hashed, resolvedBase);
|
|
194
|
+
return [
|
|
195
|
+
{
|
|
196
|
+
tag: "script",
|
|
197
|
+
attrs: { type: "importmap" },
|
|
198
|
+
children: JSON.stringify({ imports }, null, 6),
|
|
199
|
+
injectTo: "head-prepend",
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
},
|
|
203
|
+
async generateBundle() {
|
|
204
|
+
const hashed = await getHashedShims();
|
|
205
|
+
for (const [, shimInfo] of hashed) {
|
|
206
|
+
this.emitFile({
|
|
207
|
+
type: "asset",
|
|
208
|
+
fileName: shimInfo.fileName,
|
|
209
|
+
source: shimInfo.content,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
};
|
package/dist/vite/config.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Default admin gateway URL (staging environment).
|
|
4
|
+
* Used by the env injection plugin when no explicit value is configured.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ADMIN_GATEWAY_STAGING_URL = "https://admin-bff-stg.nsx.dev";
|
|
2
7
|
/**
|
|
3
8
|
* Shared dependencies that are externalized in module builds.
|
|
4
9
|
* These are provided by the shell via import maps.
|
|
10
|
+
* Derived from SHARED_DEPS_CONFIG — the single source of truth.
|
|
5
11
|
*/
|
|
6
|
-
export declare const SHARED_EXTERNALS: readonly [
|
|
12
|
+
export declare const SHARED_EXTERNALS: readonly string[];
|
|
7
13
|
export interface AdminModuleOptions {
|
|
8
14
|
/**
|
|
9
15
|
* Entry file for the module SPA
|
|
@@ -27,6 +33,15 @@ export interface AdminModuleOptions {
|
|
|
27
33
|
* - "always": Always apply during `vite build`. Used by defineModuleConfig().
|
|
28
34
|
*/
|
|
29
35
|
buildMode?: "auto" | "always";
|
|
36
|
+
/**
|
|
37
|
+
* Admin gateway URL for BFF token integration (InMemory auth).
|
|
38
|
+
* - `undefined` (default): auto-inject staging URL (`https://admin-bff-stg.nsx.dev`)
|
|
39
|
+
* - `string`: inject the provided URL
|
|
40
|
+
* - `null`: disable automatic injection entirely
|
|
41
|
+
*
|
|
42
|
+
* Explicit env vars (`.env` files, shell env) always take precedence over this option.
|
|
43
|
+
*/
|
|
44
|
+
gatewayUrl?: string | null;
|
|
30
45
|
}
|
|
31
46
|
export interface ModuleConfigOptions extends AdminModuleOptions {
|
|
32
47
|
/**
|
|
@@ -110,4 +125,3 @@ export declare function adminModule(options?: AdminModuleOptions): Plugin[];
|
|
|
110
125
|
* ```
|
|
111
126
|
*/
|
|
112
127
|
export declare function defineModuleConfig(options: ModuleConfigOptions): any;
|
|
113
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/vite/config.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
import { loadEnv } from "vite";
|
|
1
2
|
import { generateModuleManifestPlugin, serveDistPlugin } from "./plugins.js";
|
|
3
|
+
import { adminModuleI18nPlugin } from "./i18n-plugin.js";
|
|
4
|
+
import { getSharedExternals } from "./AdminShellSharedDeps.js";
|
|
5
|
+
const ENV_KEY = "VITE_ADMIN_GATEWAY_URL";
|
|
6
|
+
/**
|
|
7
|
+
* Default admin gateway URL (staging environment).
|
|
8
|
+
* Used by the env injection plugin when no explicit value is configured.
|
|
9
|
+
*/
|
|
10
|
+
export const ADMIN_GATEWAY_STAGING_URL = "https://admin-bff-stg.nsx.dev";
|
|
2
11
|
/**
|
|
3
12
|
* Shared dependencies that are externalized in module builds.
|
|
4
13
|
* These are provided by the shell via import maps.
|
|
14
|
+
* Derived from SHARED_DEPS_CONFIG — the single source of truth.
|
|
5
15
|
*/
|
|
6
|
-
export const SHARED_EXTERNALS =
|
|
7
|
-
"react",
|
|
8
|
-
"react-dom",
|
|
9
|
-
"react-router-dom",
|
|
10
|
-
"i18next",
|
|
11
|
-
"react-i18next",
|
|
12
|
-
];
|
|
16
|
+
export const SHARED_EXTERNALS = getSharedExternals();
|
|
13
17
|
/**
|
|
14
18
|
* Composable Vite plugin array for admin modules.
|
|
15
19
|
* Returns plugins that handle build-time lib config, dist serving, and manifest generation.
|
|
@@ -43,13 +47,33 @@ export const SHARED_EXTERNALS = [
|
|
|
43
47
|
* ```
|
|
44
48
|
*/
|
|
45
49
|
export function adminModule(options = {}) {
|
|
46
|
-
const { entry = "./src/spa.tsx", outDir = "dist", additionalExternals = [], buildMode = "auto", } = options;
|
|
50
|
+
const { entry = "./src/spa.tsx", outDir = "dist", additionalExternals = [], buildMode = "auto", gatewayUrl, } = options;
|
|
47
51
|
const externals = [...SHARED_EXTERNALS, ...additionalExternals];
|
|
48
52
|
function isModuleBuild() {
|
|
49
53
|
if (buildMode === "always")
|
|
50
54
|
return true;
|
|
51
55
|
return process.env.ADMIN_MODULE === "true";
|
|
52
56
|
}
|
|
57
|
+
const envPlugin = {
|
|
58
|
+
name: "admin-module-env",
|
|
59
|
+
config(_config, { mode }) {
|
|
60
|
+
if (gatewayUrl === null)
|
|
61
|
+
return;
|
|
62
|
+
const env = loadEnv(mode, process.cwd(), "VITE_");
|
|
63
|
+
const fromEnv = process.env[ENV_KEY] || env[ENV_KEY];
|
|
64
|
+
if (fromEnv) {
|
|
65
|
+
console.log(`[admin-module-env] ${ENV_KEY} → ${fromEnv} (from env)`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const url = gatewayUrl ?? ADMIN_GATEWAY_STAGING_URL;
|
|
69
|
+
console.log(`[admin-module-env] ${ENV_KEY} → ${url} (auto-injected)`);
|
|
70
|
+
return {
|
|
71
|
+
define: {
|
|
72
|
+
[`import.meta.env.${ENV_KEY}`]: JSON.stringify(url),
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
53
77
|
const buildConfigPlugin = {
|
|
54
78
|
name: "admin-module-build-config",
|
|
55
79
|
config(_config, { command }) {
|
|
@@ -76,6 +100,8 @@ export function adminModule(options = {}) {
|
|
|
76
100
|
},
|
|
77
101
|
};
|
|
78
102
|
return [
|
|
103
|
+
envPlugin,
|
|
104
|
+
adminModuleI18nPlugin(),
|
|
79
105
|
serveDistPlugin({ distDir: outDir }),
|
|
80
106
|
generateModuleManifestPlugin(),
|
|
81
107
|
buildConfigPlugin,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for admin module i18n:
|
|
3
|
+
* - Validates all 4 locale files exist at build start
|
|
4
|
+
* - Auto-injects registerModuleTranslations into spa.tsx and main.tsx entry points
|
|
5
|
+
*/
|
|
6
|
+
import type { Plugin } from "vite";
|
|
7
|
+
export interface AdminModuleI18nPluginOptions {
|
|
8
|
+
/** Path to admin.module.json */
|
|
9
|
+
manifestPath?: string;
|
|
10
|
+
/** Root directory (project root) */
|
|
11
|
+
root?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function adminModuleI18nPlugin(options?: AdminModuleI18nPluginOptions): Plugin;
|