@hai3/framework 0.2.0-alpha.1 → 0.2.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.
- package/CLAUDE.md +31 -2
- package/commands/hai3-quick-ref.md +1 -3
- package/dist/index.cjs +454 -217
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +161 -83
- package/dist/index.d.ts +161 -83
- package/dist/index.js +443 -202
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +86 -15
- package/dist/types.d.ts +86 -15
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -159,8 +159,8 @@ Add the missing plugin: .use(${depName}())`
|
|
|
159
159
|
*/
|
|
160
160
|
createStoreWithSlices(slices) {
|
|
161
161
|
const store = getStore();
|
|
162
|
-
slices.forEach((
|
|
163
|
-
registerSlice(
|
|
162
|
+
slices.forEach((slice10) => {
|
|
163
|
+
registerSlice(slice10);
|
|
164
164
|
});
|
|
165
165
|
return store;
|
|
166
166
|
}
|
|
@@ -444,6 +444,25 @@ var tenantSlice = slice8;
|
|
|
444
444
|
var tenantActions = { setTenant, setTenantLoading, clearTenant };
|
|
445
445
|
var tenantSlice_default = slice8.reducer;
|
|
446
446
|
|
|
447
|
+
// src/slices/mockSlice.ts
|
|
448
|
+
import { createSlice as createSlice9 } from "@hai3/state";
|
|
449
|
+
var SLICE_KEY9 = "mock";
|
|
450
|
+
var initialState9 = {
|
|
451
|
+
enabled: false
|
|
452
|
+
};
|
|
453
|
+
var { slice: slice9, setMockEnabled } = createSlice9({
|
|
454
|
+
name: SLICE_KEY9,
|
|
455
|
+
initialState: initialState9,
|
|
456
|
+
reducers: {
|
|
457
|
+
setMockEnabled: (state, action) => {
|
|
458
|
+
state.enabled = action.payload;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
var mockSlice = slice9;
|
|
463
|
+
var mockActions = { setMockEnabled };
|
|
464
|
+
var mockSlice_default = slice9.reducer;
|
|
465
|
+
|
|
447
466
|
// src/slices/index.ts
|
|
448
467
|
var LAYOUT_SLICE_NAME = "layout";
|
|
449
468
|
var TENANT_SLICE_NAME = "app/tenant";
|
|
@@ -489,39 +508,34 @@ function screensets(config) {
|
|
|
489
508
|
// src/plugins/themes.ts
|
|
490
509
|
import { eventBus } from "@hai3/state";
|
|
491
510
|
|
|
492
|
-
// src/compat.ts
|
|
493
|
-
import { getStore as getStore2 } from "@hai3/state";
|
|
494
|
-
import { apiRegistry as apiRegistry2 } from "@hai3/api";
|
|
495
|
-
import { screensetRegistry as sdkScreensetRegistry2 } from "@hai3/screensets";
|
|
496
|
-
|
|
497
511
|
// src/registries/themeRegistry.ts
|
|
498
|
-
function createThemeRegistry() {
|
|
512
|
+
function createThemeRegistry(config) {
|
|
499
513
|
const themes2 = /* @__PURE__ */ new Map();
|
|
500
|
-
const
|
|
514
|
+
const uikitThemes = /* @__PURE__ */ new Map();
|
|
501
515
|
let currentThemeId = null;
|
|
502
|
-
|
|
516
|
+
const customApplyFn = config?.applyFn ?? null;
|
|
503
517
|
const subscribers = /* @__PURE__ */ new Set();
|
|
504
518
|
let version = 0;
|
|
505
519
|
function notifySubscribers() {
|
|
506
520
|
version++;
|
|
507
521
|
subscribers.forEach((callback) => callback());
|
|
508
522
|
}
|
|
509
|
-
function applyCSSVariables(
|
|
523
|
+
function applyCSSVariables(config2) {
|
|
510
524
|
if (typeof document === "undefined") return;
|
|
511
525
|
const root = document.documentElement;
|
|
512
|
-
Object.entries(
|
|
526
|
+
Object.entries(config2.variables).forEach(([key, value]) => {
|
|
513
527
|
root.style.setProperty(key, value);
|
|
514
528
|
});
|
|
515
529
|
}
|
|
516
530
|
return {
|
|
517
531
|
/**
|
|
518
532
|
* Register a theme.
|
|
519
|
-
* Supports both
|
|
533
|
+
* Supports both config-based API and UIKit theme API.
|
|
520
534
|
*/
|
|
521
|
-
register(configOrId,
|
|
535
|
+
register(configOrId, uikitTheme) {
|
|
522
536
|
if (typeof configOrId === "string") {
|
|
523
537
|
const id = configOrId;
|
|
524
|
-
if (!
|
|
538
|
+
if (!uikitTheme) {
|
|
525
539
|
console.warn(`register() called with ID "${id}" but no theme object. Skipping.`);
|
|
526
540
|
return;
|
|
527
541
|
}
|
|
@@ -529,39 +543,33 @@ function createThemeRegistry() {
|
|
|
529
543
|
console.warn(`Theme "${id}" is already registered. Skipping.`);
|
|
530
544
|
return;
|
|
531
545
|
}
|
|
532
|
-
|
|
546
|
+
uikitThemes.set(id, uikitTheme);
|
|
533
547
|
let themeName = id;
|
|
534
|
-
if (
|
|
535
|
-
const nameValue =
|
|
548
|
+
if (uikitTheme && typeof uikitTheme === "object" && "name" in uikitTheme) {
|
|
549
|
+
const nameValue = uikitTheme.name;
|
|
536
550
|
if (typeof nameValue === "string") {
|
|
537
551
|
themeName = nameValue;
|
|
538
552
|
}
|
|
539
553
|
}
|
|
540
|
-
const
|
|
554
|
+
const config3 = {
|
|
541
555
|
id,
|
|
542
556
|
name: themeName,
|
|
543
557
|
variables: {}
|
|
544
|
-
//
|
|
558
|
+
// UIKit themes use custom apply function
|
|
545
559
|
};
|
|
546
|
-
themes2.set(id,
|
|
560
|
+
themes2.set(id, config3);
|
|
547
561
|
return;
|
|
548
562
|
}
|
|
549
|
-
const
|
|
550
|
-
if (themes2.has(
|
|
551
|
-
console.warn(`Theme "${
|
|
563
|
+
const config2 = configOrId;
|
|
564
|
+
if (themes2.has(config2.id)) {
|
|
565
|
+
console.warn(`Theme "${config2.id}" is already registered. Skipping.`);
|
|
552
566
|
return;
|
|
553
567
|
}
|
|
554
|
-
themes2.set(
|
|
555
|
-
if (
|
|
556
|
-
this.apply(
|
|
568
|
+
themes2.set(config2.id, config2);
|
|
569
|
+
if (config2.default && currentThemeId === null) {
|
|
570
|
+
this.apply(config2.id);
|
|
557
571
|
}
|
|
558
572
|
},
|
|
559
|
-
/**
|
|
560
|
-
* Set the apply function (legacy API).
|
|
561
|
-
*/
|
|
562
|
-
setApplyFunction(applyFn) {
|
|
563
|
-
customApplyFn = applyFn;
|
|
564
|
-
},
|
|
565
573
|
/**
|
|
566
574
|
* Get theme by ID.
|
|
567
575
|
*/
|
|
@@ -578,16 +586,17 @@ function createThemeRegistry() {
|
|
|
578
586
|
* Apply a theme.
|
|
579
587
|
*/
|
|
580
588
|
apply(id) {
|
|
581
|
-
const
|
|
582
|
-
if (!
|
|
589
|
+
const config2 = themes2.get(id);
|
|
590
|
+
if (!config2) {
|
|
583
591
|
console.warn(`Theme "${id}" not found. Cannot apply.`);
|
|
584
592
|
return;
|
|
585
593
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
594
|
+
if (config2.variables && Object.keys(config2.variables).length > 0) {
|
|
595
|
+
applyCSSVariables(config2);
|
|
596
|
+
}
|
|
597
|
+
const uikitTheme = uikitThemes.get(id);
|
|
598
|
+
if (uikitTheme && customApplyFn) {
|
|
599
|
+
customApplyFn(uikitTheme, id);
|
|
591
600
|
}
|
|
592
601
|
currentThemeId = id;
|
|
593
602
|
notifySubscribers();
|
|
@@ -618,117 +627,18 @@ function createThemeRegistry() {
|
|
|
618
627
|
};
|
|
619
628
|
}
|
|
620
629
|
|
|
621
|
-
// src/registries/routeRegistry.ts
|
|
622
|
-
function createRouteRegistry(screensetRegistry3) {
|
|
623
|
-
let routes = null;
|
|
624
|
-
function buildRoutes() {
|
|
625
|
-
if (routes !== null) {
|
|
626
|
-
return routes;
|
|
627
|
-
}
|
|
628
|
-
routes = [];
|
|
629
|
-
const screensets2 = screensetRegistry3.getAll();
|
|
630
|
-
screensets2.forEach((screenset) => {
|
|
631
|
-
screenset.menu.forEach((menuScreenItem) => {
|
|
632
|
-
const screenId = menuScreenItem.menuItem.screenId ?? menuScreenItem.menuItem.id;
|
|
633
|
-
if (screenId && menuScreenItem.screen) {
|
|
634
|
-
routes.push({
|
|
635
|
-
screensetId: screenset.id,
|
|
636
|
-
screenId,
|
|
637
|
-
loader: menuScreenItem.screen
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
});
|
|
642
|
-
return routes;
|
|
643
|
-
}
|
|
644
|
-
return {
|
|
645
|
-
/**
|
|
646
|
-
* Check if a screen exists by screenId only (globally unique).
|
|
647
|
-
*/
|
|
648
|
-
hasScreenById(screenId) {
|
|
649
|
-
const allRoutes = buildRoutes();
|
|
650
|
-
return allRoutes.some((route) => route.screenId === screenId);
|
|
651
|
-
},
|
|
652
|
-
/**
|
|
653
|
-
* Check if a screen exists (legacy, requires both IDs).
|
|
654
|
-
*/
|
|
655
|
-
hasScreen(screensetId, screenId) {
|
|
656
|
-
const allRoutes = buildRoutes();
|
|
657
|
-
return allRoutes.some(
|
|
658
|
-
(route) => route.screensetId === screensetId && route.screenId === screenId
|
|
659
|
-
);
|
|
660
|
-
},
|
|
661
|
-
/**
|
|
662
|
-
* Get screenset ID for a given screen ID (reverse lookup).
|
|
663
|
-
* Screen IDs are globally unique across all screensets.
|
|
664
|
-
*/
|
|
665
|
-
getScreensetForScreen(screenId) {
|
|
666
|
-
const allRoutes = buildRoutes();
|
|
667
|
-
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
668
|
-
return route?.screensetId;
|
|
669
|
-
},
|
|
670
|
-
/**
|
|
671
|
-
* Get screen loader by screenId only.
|
|
672
|
-
*/
|
|
673
|
-
getScreenById(screenId) {
|
|
674
|
-
const allRoutes = buildRoutes();
|
|
675
|
-
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
676
|
-
return route?.loader;
|
|
677
|
-
},
|
|
678
|
-
/**
|
|
679
|
-
* Get screen loader (legacy, requires both IDs).
|
|
680
|
-
*/
|
|
681
|
-
getScreen(screensetId, screenId) {
|
|
682
|
-
const allRoutes = buildRoutes();
|
|
683
|
-
const route = allRoutes.find(
|
|
684
|
-
(r) => r.screensetId === screensetId && r.screenId === screenId
|
|
685
|
-
);
|
|
686
|
-
return route?.loader;
|
|
687
|
-
},
|
|
688
|
-
/**
|
|
689
|
-
* Get all routes.
|
|
690
|
-
*/
|
|
691
|
-
getAll() {
|
|
692
|
-
const allRoutes = buildRoutes();
|
|
693
|
-
return allRoutes.map(({ screensetId, screenId }) => ({
|
|
694
|
-
screensetId,
|
|
695
|
-
screenId
|
|
696
|
-
}));
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// src/compat.ts
|
|
702
|
-
import { screensetRegistry } from "@hai3/screensets";
|
|
703
|
-
var screenActions3 = screenActions;
|
|
704
|
-
var ACCOUNTS_DOMAIN = "accounts";
|
|
705
|
-
var themeRegistry = createThemeRegistry();
|
|
706
|
-
var routeRegistry = createRouteRegistry(sdkScreensetRegistry2);
|
|
707
|
-
var navigateToScreen = (screenId) => {
|
|
708
|
-
getStore2().dispatch(screenActions3.setActiveScreen(screenId));
|
|
709
|
-
};
|
|
710
|
-
var fetchCurrentUser = () => (_dispatch) => {
|
|
711
|
-
try {
|
|
712
|
-
const accountsService = apiRegistry2.getService(ACCOUNTS_DOMAIN);
|
|
713
|
-
const service = accountsService;
|
|
714
|
-
service.getCurrentUser?.();
|
|
715
|
-
} catch {
|
|
716
|
-
console.warn("fetchCurrentUser: accounts service not registered");
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
|
|
720
630
|
// src/plugins/themes.ts
|
|
721
631
|
function changeTheme(payload) {
|
|
722
632
|
eventBus.emit("theme/changed", payload);
|
|
723
633
|
}
|
|
724
|
-
function themes() {
|
|
725
|
-
const
|
|
634
|
+
function themes(config) {
|
|
635
|
+
const themeRegistry = createThemeRegistry(config);
|
|
726
636
|
return {
|
|
727
637
|
name: "themes",
|
|
728
638
|
dependencies: [],
|
|
729
639
|
provides: {
|
|
730
640
|
registries: {
|
|
731
|
-
themeRegistry
|
|
641
|
+
themeRegistry
|
|
732
642
|
},
|
|
733
643
|
actions: {
|
|
734
644
|
changeTheme
|
|
@@ -736,11 +646,11 @@ function themes() {
|
|
|
736
646
|
},
|
|
737
647
|
onInit(_app) {
|
|
738
648
|
eventBus.on("theme/changed", (payload) => {
|
|
739
|
-
|
|
649
|
+
themeRegistry.apply(payload.themeId);
|
|
740
650
|
});
|
|
741
|
-
const themes2 =
|
|
651
|
+
const themes2 = themeRegistry.getAll();
|
|
742
652
|
if (themes2.length > 0) {
|
|
743
|
-
|
|
653
|
+
themeRegistry.apply(themes2[0].id);
|
|
744
654
|
}
|
|
745
655
|
}
|
|
746
656
|
};
|
|
@@ -874,10 +784,48 @@ function resolveBase(pluginConfig, appConfig) {
|
|
|
874
784
|
return normalizeBase(rawBase);
|
|
875
785
|
}
|
|
876
786
|
|
|
787
|
+
// src/utils/routeMatcher.ts
|
|
788
|
+
import { match, compile } from "path-to-regexp";
|
|
789
|
+
function compileRoute(pattern, screensetId, screenId) {
|
|
790
|
+
return {
|
|
791
|
+
pattern,
|
|
792
|
+
screensetId,
|
|
793
|
+
screenId,
|
|
794
|
+
matcher: match(pattern, { decode: decodeURIComponent }),
|
|
795
|
+
toPath: compile(pattern, { encode: encodeURIComponent })
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function matchPath(path, routes) {
|
|
799
|
+
for (const route of routes) {
|
|
800
|
+
const result = route.matcher(path);
|
|
801
|
+
if (result) {
|
|
802
|
+
return {
|
|
803
|
+
screensetId: route.screensetId,
|
|
804
|
+
screenId: route.screenId,
|
|
805
|
+
params: result.params
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return void 0;
|
|
810
|
+
}
|
|
811
|
+
function generatePath(route, params) {
|
|
812
|
+
return route.toPath(params);
|
|
813
|
+
}
|
|
814
|
+
function extractRequiredParams(pattern) {
|
|
815
|
+
const params = [];
|
|
816
|
+
const matches = pattern.match(/:([^/]+)/g);
|
|
817
|
+
if (matches) {
|
|
818
|
+
for (const match2 of matches) {
|
|
819
|
+
params.push(match2.slice(1));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return params;
|
|
823
|
+
}
|
|
824
|
+
|
|
877
825
|
// src/plugins/navigation.ts
|
|
878
|
-
var
|
|
826
|
+
var screenActions3 = screenActions;
|
|
879
827
|
var menuActions3 = menuActions;
|
|
880
|
-
function
|
|
828
|
+
function navigateToScreen(payload) {
|
|
881
829
|
eventBus3.emit("navigation/screen/navigated", payload);
|
|
882
830
|
}
|
|
883
831
|
function navigateToScreenset(payload) {
|
|
@@ -889,14 +837,46 @@ function navigation(config) {
|
|
|
889
837
|
dependencies: ["screensets"],
|
|
890
838
|
provides: {
|
|
891
839
|
actions: {
|
|
892
|
-
navigateToScreen
|
|
840
|
+
navigateToScreen,
|
|
893
841
|
navigateToScreenset
|
|
894
842
|
}
|
|
895
843
|
},
|
|
896
844
|
onInit(app) {
|
|
897
845
|
const dispatch = app.store.dispatch;
|
|
898
846
|
const base = resolveBase(config, app.config);
|
|
847
|
+
const routerMode = app.config.routerMode ?? "browser";
|
|
899
848
|
let currentScreensetId = null;
|
|
849
|
+
const getCurrentPath = () => {
|
|
850
|
+
if (typeof window === "undefined") return "/";
|
|
851
|
+
switch (routerMode) {
|
|
852
|
+
case "hash":
|
|
853
|
+
return window.location.hash.slice(1) || "/";
|
|
854
|
+
case "memory":
|
|
855
|
+
return "/";
|
|
856
|
+
case "browser":
|
|
857
|
+
default:
|
|
858
|
+
return window.location.pathname;
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
const updateURL = (path) => {
|
|
862
|
+
if (typeof window === "undefined" || routerMode === "memory") {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const url = prependBase(path, base);
|
|
866
|
+
switch (routerMode) {
|
|
867
|
+
case "hash":
|
|
868
|
+
if (window.location.hash !== `#${url}`) {
|
|
869
|
+
window.location.hash = url;
|
|
870
|
+
}
|
|
871
|
+
break;
|
|
872
|
+
case "browser":
|
|
873
|
+
default:
|
|
874
|
+
if (window.location.pathname !== url) {
|
|
875
|
+
window.history.pushState(null, "", url);
|
|
876
|
+
}
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
};
|
|
900
880
|
const loadScreensetTranslations = async (screensetId, language) => {
|
|
901
881
|
await i18nRegistry.loadScreensetTranslations(
|
|
902
882
|
screensetId,
|
|
@@ -925,21 +905,17 @@ function navigation(config) {
|
|
|
925
905
|
});
|
|
926
906
|
updateScreensetMenu(screenset);
|
|
927
907
|
};
|
|
928
|
-
const
|
|
929
|
-
const
|
|
930
|
-
|
|
931
|
-
return null;
|
|
932
|
-
}
|
|
933
|
-
const parts = internalPath.split("/").filter(Boolean);
|
|
934
|
-
return parts[0] || null;
|
|
908
|
+
const extractInternalPath = () => {
|
|
909
|
+
const currentPath = getCurrentPath();
|
|
910
|
+
return stripBase(currentPath, base) || "/";
|
|
935
911
|
};
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
912
|
+
const activateFromRouteMatch = (match2) => {
|
|
913
|
+
activateScreenset(match2.screensetId);
|
|
914
|
+
dispatch(screenActions3.navigateTo(match2.screenId));
|
|
915
|
+
};
|
|
916
|
+
const matchCurrentPath = () => {
|
|
917
|
+
const path = extractInternalPath();
|
|
918
|
+
return app.routeRegistry?.matchRoute(path);
|
|
943
919
|
};
|
|
944
920
|
eventBus3.on("navigation/screen/navigated", (payload) => {
|
|
945
921
|
if (!app.routeRegistry?.hasScreen(payload.screensetId, payload.screenId)) {
|
|
@@ -948,14 +924,22 @@ function navigation(config) {
|
|
|
948
924
|
);
|
|
949
925
|
return;
|
|
950
926
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
927
|
+
const pattern = app.routeRegistry?.getRoutePattern(payload.screenId);
|
|
928
|
+
if (pattern) {
|
|
929
|
+
const requiredParams = extractRequiredParams(pattern);
|
|
930
|
+
const providedParams = payload.params || {};
|
|
931
|
+
const missingParams = requiredParams.filter((param) => !(param in providedParams));
|
|
932
|
+
if (missingParams.length > 0) {
|
|
933
|
+
console.warn(
|
|
934
|
+
`Screen "${payload.screenId}" requires route params [${requiredParams.join(", ")}] but missing: [${missingParams.join(", ")}]`
|
|
935
|
+
);
|
|
936
|
+
return;
|
|
957
937
|
}
|
|
958
938
|
}
|
|
939
|
+
activateScreenset(payload.screensetId);
|
|
940
|
+
dispatch(screenActions3.navigateTo(payload.screenId));
|
|
941
|
+
const path = app.routeRegistry?.generatePath(payload.screenId, payload.params) ?? `/${payload.screenId}`;
|
|
942
|
+
updateURL(path);
|
|
959
943
|
});
|
|
960
944
|
eventBus3.on("navigation/screenset/navigated", (payload) => {
|
|
961
945
|
const screenset = app.screensetRegistry.get(payload.screensetId);
|
|
@@ -963,7 +947,7 @@ function navigation(config) {
|
|
|
963
947
|
console.warn(`Screenset "${payload.screensetId}" not found.`);
|
|
964
948
|
return;
|
|
965
949
|
}
|
|
966
|
-
|
|
950
|
+
navigateToScreen({
|
|
967
951
|
screensetId: payload.screensetId,
|
|
968
952
|
screenId: screenset.defaultScreen
|
|
969
953
|
});
|
|
@@ -989,28 +973,161 @@ function navigation(config) {
|
|
|
989
973
|
);
|
|
990
974
|
});
|
|
991
975
|
});
|
|
992
|
-
if (typeof window !== "undefined") {
|
|
993
|
-
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
996
|
-
|
|
976
|
+
if (typeof window !== "undefined" && routerMode !== "memory") {
|
|
977
|
+
const handleURLChange = () => {
|
|
978
|
+
const match3 = matchCurrentPath();
|
|
979
|
+
if (match3) {
|
|
980
|
+
activateFromRouteMatch(match3);
|
|
997
981
|
}
|
|
998
|
-
}
|
|
999
|
-
|
|
982
|
+
};
|
|
983
|
+
if (routerMode === "hash") {
|
|
984
|
+
window.addEventListener("hashchange", handleURLChange);
|
|
985
|
+
} else {
|
|
986
|
+
window.addEventListener("popstate", handleURLChange);
|
|
987
|
+
}
|
|
988
|
+
const match2 = matchCurrentPath();
|
|
1000
989
|
const autoNavigate = app.config.autoNavigate !== false;
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
990
|
+
if (match2) {
|
|
991
|
+
activateFromRouteMatch(match2);
|
|
1003
992
|
} else if (autoNavigate) {
|
|
1004
993
|
const screensets2 = app.screensetRegistry.getAll();
|
|
1005
994
|
if (screensets2.length > 0) {
|
|
1006
995
|
navigateToScreenset({ screensetId: screensets2[0].id });
|
|
1007
996
|
}
|
|
1008
997
|
}
|
|
998
|
+
} else if (routerMode === "memory") {
|
|
999
|
+
const autoNavigate = app.config.autoNavigate !== false;
|
|
1000
|
+
if (autoNavigate) {
|
|
1001
|
+
const screensets2 = app.screensetRegistry.getAll();
|
|
1002
|
+
if (screensets2.length > 0) {
|
|
1003
|
+
navigateToScreenset({ screensetId: screensets2[0].id });
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1009
1006
|
}
|
|
1010
1007
|
}
|
|
1011
1008
|
};
|
|
1012
1009
|
}
|
|
1013
1010
|
|
|
1011
|
+
// src/registries/routeRegistry.ts
|
|
1012
|
+
function createRouteRegistry(screensetRegistry3) {
|
|
1013
|
+
let routes = null;
|
|
1014
|
+
function buildRoutes() {
|
|
1015
|
+
if (routes !== null) {
|
|
1016
|
+
return routes;
|
|
1017
|
+
}
|
|
1018
|
+
routes = [];
|
|
1019
|
+
const screensets2 = screensetRegistry3.getAll();
|
|
1020
|
+
screensets2.forEach((screenset) => {
|
|
1021
|
+
screenset.menu.forEach((menuScreenItem) => {
|
|
1022
|
+
const screenId = menuScreenItem.menuItem.screenId ?? menuScreenItem.menuItem.id;
|
|
1023
|
+
if (screenId && menuScreenItem.screen) {
|
|
1024
|
+
const pattern = menuScreenItem.menuItem.path ?? `/${screenId}`;
|
|
1025
|
+
const compiledRoute = compileRoute(pattern, screenset.id, screenId);
|
|
1026
|
+
routes.push({
|
|
1027
|
+
screensetId: screenset.id,
|
|
1028
|
+
screenId,
|
|
1029
|
+
loader: menuScreenItem.screen,
|
|
1030
|
+
pattern,
|
|
1031
|
+
compiledRoute
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
routes.sort((a, b) => {
|
|
1037
|
+
const aHasParams = a.pattern.includes(":");
|
|
1038
|
+
const bHasParams = b.pattern.includes(":");
|
|
1039
|
+
if (!aHasParams && bHasParams) return -1;
|
|
1040
|
+
if (aHasParams && !bHasParams) return 1;
|
|
1041
|
+
return 0;
|
|
1042
|
+
});
|
|
1043
|
+
return routes;
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
/**
|
|
1047
|
+
* Check if a screen exists by screenId only (globally unique).
|
|
1048
|
+
*/
|
|
1049
|
+
hasScreenById(screenId) {
|
|
1050
|
+
const allRoutes = buildRoutes();
|
|
1051
|
+
return allRoutes.some((route) => route.screenId === screenId);
|
|
1052
|
+
},
|
|
1053
|
+
/**
|
|
1054
|
+
* Check if a screen exists by both screensetId and screenId (explicit lookup when screenset context is known).
|
|
1055
|
+
*/
|
|
1056
|
+
hasScreen(screensetId, screenId) {
|
|
1057
|
+
const allRoutes = buildRoutes();
|
|
1058
|
+
return allRoutes.some(
|
|
1059
|
+
(route) => route.screensetId === screensetId && route.screenId === screenId
|
|
1060
|
+
);
|
|
1061
|
+
},
|
|
1062
|
+
/**
|
|
1063
|
+
* Get screenset ID for a given screen ID (reverse lookup).
|
|
1064
|
+
* Screen IDs are globally unique across all screensets.
|
|
1065
|
+
*/
|
|
1066
|
+
getScreensetForScreen(screenId) {
|
|
1067
|
+
const allRoutes = buildRoutes();
|
|
1068
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
1069
|
+
return route?.screensetId;
|
|
1070
|
+
},
|
|
1071
|
+
/**
|
|
1072
|
+
* Get screen loader by screenId only.
|
|
1073
|
+
*/
|
|
1074
|
+
getScreenById(screenId) {
|
|
1075
|
+
const allRoutes = buildRoutes();
|
|
1076
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
1077
|
+
return route?.loader;
|
|
1078
|
+
},
|
|
1079
|
+
/**
|
|
1080
|
+
* Get screen loader by both screensetId and screenId (explicit lookup when screenset context is known).
|
|
1081
|
+
*/
|
|
1082
|
+
getScreen(screensetId, screenId) {
|
|
1083
|
+
const allRoutes = buildRoutes();
|
|
1084
|
+
const route = allRoutes.find(
|
|
1085
|
+
(r) => r.screensetId === screensetId && r.screenId === screenId
|
|
1086
|
+
);
|
|
1087
|
+
return route?.loader;
|
|
1088
|
+
},
|
|
1089
|
+
/**
|
|
1090
|
+
* Get all routes.
|
|
1091
|
+
*/
|
|
1092
|
+
getAll() {
|
|
1093
|
+
const allRoutes = buildRoutes();
|
|
1094
|
+
return allRoutes.map(({ screensetId, screenId }) => ({
|
|
1095
|
+
screensetId,
|
|
1096
|
+
screenId
|
|
1097
|
+
}));
|
|
1098
|
+
},
|
|
1099
|
+
/**
|
|
1100
|
+
* Match a URL path against registered routes, extracting params.
|
|
1101
|
+
* Returns the first matching route with extracted params, or undefined if no match.
|
|
1102
|
+
*/
|
|
1103
|
+
matchRoute(path) {
|
|
1104
|
+
const allRoutes = buildRoutes();
|
|
1105
|
+
const compiledRoutes = allRoutes.map((r) => r.compiledRoute);
|
|
1106
|
+
return matchPath(path, compiledRoutes);
|
|
1107
|
+
},
|
|
1108
|
+
/**
|
|
1109
|
+
* Generate a URL path for a screen with given params.
|
|
1110
|
+
*/
|
|
1111
|
+
generatePath(screenId, params = {}) {
|
|
1112
|
+
const allRoutes = buildRoutes();
|
|
1113
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
1114
|
+
if (!route) {
|
|
1115
|
+
console.warn(`Route not found for screen: ${screenId}`);
|
|
1116
|
+
return `/${screenId}`;
|
|
1117
|
+
}
|
|
1118
|
+
return generatePath(route.compiledRoute, params);
|
|
1119
|
+
},
|
|
1120
|
+
/**
|
|
1121
|
+
* Get the route pattern for a screen.
|
|
1122
|
+
*/
|
|
1123
|
+
getRoutePattern(screenId) {
|
|
1124
|
+
const allRoutes = buildRoutes();
|
|
1125
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
1126
|
+
return route?.pattern;
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1014
1131
|
// src/plugins/routing.ts
|
|
1015
1132
|
function routing() {
|
|
1016
1133
|
return {
|
|
@@ -1019,8 +1136,8 @@ function routing() {
|
|
|
1019
1136
|
onRegister(_app) {
|
|
1020
1137
|
},
|
|
1021
1138
|
onInit(app) {
|
|
1022
|
-
const
|
|
1023
|
-
app.routeRegistry =
|
|
1139
|
+
const routeRegistry = createRouteRegistry(app.screensetRegistry);
|
|
1140
|
+
app.routeRegistry = routeRegistry;
|
|
1024
1141
|
}
|
|
1025
1142
|
};
|
|
1026
1143
|
}
|
|
@@ -1068,16 +1185,101 @@ function effects() {
|
|
|
1068
1185
|
};
|
|
1069
1186
|
}
|
|
1070
1187
|
|
|
1188
|
+
// src/effects/mockEffects.ts
|
|
1189
|
+
import { eventBus as eventBus5, getStore as getStore2 } from "@hai3/state";
|
|
1190
|
+
import { apiRegistry as apiRegistry2, isMockPlugin } from "@hai3/api";
|
|
1191
|
+
function hasPluginManagement(protocol) {
|
|
1192
|
+
return "plugins" in protocol && typeof protocol.plugins === "object";
|
|
1193
|
+
}
|
|
1194
|
+
var MockEvents = {
|
|
1195
|
+
Toggle: "mock/toggle"
|
|
1196
|
+
};
|
|
1197
|
+
function syncMockPlugins(enabled) {
|
|
1198
|
+
for (const service of apiRegistry2.getAll()) {
|
|
1199
|
+
const registeredPlugins = service.getPlugins();
|
|
1200
|
+
for (const [protocol, plugins] of registeredPlugins) {
|
|
1201
|
+
if (!hasPluginManagement(protocol)) continue;
|
|
1202
|
+
for (const plugin of plugins) {
|
|
1203
|
+
if (isMockPlugin(plugin)) {
|
|
1204
|
+
if (enabled) {
|
|
1205
|
+
const existingPlugins = protocol.plugins.getAll();
|
|
1206
|
+
if (!existingPlugins.includes(plugin)) {
|
|
1207
|
+
protocol.plugins.add(plugin);
|
|
1208
|
+
}
|
|
1209
|
+
} else {
|
|
1210
|
+
protocol.plugins.remove(plugin);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function initMockEffects() {
|
|
1218
|
+
const store = getStore2();
|
|
1219
|
+
const unsubscribe = eventBus5.on(MockEvents.Toggle, (payload) => {
|
|
1220
|
+
store.dispatch(setMockEnabled(payload.enabled));
|
|
1221
|
+
syncMockPlugins(payload.enabled);
|
|
1222
|
+
});
|
|
1223
|
+
const currentState = store.getState();
|
|
1224
|
+
if ("mock" in currentState && currentState.mock && typeof currentState.mock === "object" && "enabled" in currentState.mock) {
|
|
1225
|
+
syncMockPlugins(currentState.mock.enabled);
|
|
1226
|
+
}
|
|
1227
|
+
return () => {
|
|
1228
|
+
unsubscribe.unsubscribe();
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
function toggleMockMode(enabled) {
|
|
1232
|
+
eventBus5.emit(MockEvents.Toggle, { enabled });
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// src/plugins/mock.ts
|
|
1236
|
+
var cleanup = null;
|
|
1237
|
+
function isDevEnvironment() {
|
|
1238
|
+
if (typeof window === "undefined") return false;
|
|
1239
|
+
const { hostname } = window.location;
|
|
1240
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local");
|
|
1241
|
+
}
|
|
1242
|
+
function mock(config) {
|
|
1243
|
+
return {
|
|
1244
|
+
name: "mock",
|
|
1245
|
+
dependencies: ["effects"],
|
|
1246
|
+
provides: {
|
|
1247
|
+
slices: [mockSlice],
|
|
1248
|
+
actions: {
|
|
1249
|
+
toggleMockMode
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
onInit() {
|
|
1253
|
+
cleanup = initMockEffects();
|
|
1254
|
+
const isDev = isDevEnvironment();
|
|
1255
|
+
const enabledByDefault = config?.enabledByDefault ?? isDev;
|
|
1256
|
+
if (enabledByDefault) {
|
|
1257
|
+
toggleMockMode(true);
|
|
1258
|
+
if (isDev) {
|
|
1259
|
+
console.log("[HAI3] Mock mode enabled by default (dev environment detected)");
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
onDestroy() {
|
|
1264
|
+
if (cleanup) {
|
|
1265
|
+
cleanup();
|
|
1266
|
+
cleanup = null;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1071
1272
|
// src/presets/index.ts
|
|
1072
|
-
function full() {
|
|
1273
|
+
function full(config) {
|
|
1073
1274
|
return [
|
|
1074
1275
|
effects(),
|
|
1075
1276
|
screensets({ autoDiscover: true }),
|
|
1076
|
-
themes(),
|
|
1277
|
+
themes(config?.themes),
|
|
1077
1278
|
layout(),
|
|
1078
1279
|
routing(),
|
|
1079
1280
|
navigation(),
|
|
1080
|
-
i18n()
|
|
1281
|
+
i18n(),
|
|
1282
|
+
mock()
|
|
1081
1283
|
];
|
|
1082
1284
|
}
|
|
1083
1285
|
function minimal() {
|
|
@@ -1099,31 +1301,31 @@ var presets = {
|
|
|
1099
1301
|
|
|
1100
1302
|
// src/createHAI3App.ts
|
|
1101
1303
|
function createHAI3App(config) {
|
|
1102
|
-
return createHAI3(config).useAll(full()).build();
|
|
1304
|
+
return createHAI3(config).useAll(full({ themes: config?.themes })).build();
|
|
1103
1305
|
}
|
|
1104
1306
|
|
|
1105
1307
|
// src/registries/index.ts
|
|
1106
|
-
import { createScreensetRegistry, screensetRegistry
|
|
1308
|
+
import { createScreensetRegistry, screensetRegistry } from "@hai3/screensets";
|
|
1107
1309
|
|
|
1108
1310
|
// src/index.ts
|
|
1109
|
-
import { eventBus as
|
|
1311
|
+
import { eventBus as eventBus7, createStore, getStore as getStore4, registerSlice as registerSlice2, hasSlice, createSlice as createSlice10 } from "@hai3/state";
|
|
1110
1312
|
import {
|
|
1111
1313
|
LayoutDomain,
|
|
1112
1314
|
ScreensetCategory
|
|
1113
1315
|
} from "@hai3/screensets";
|
|
1114
1316
|
|
|
1115
1317
|
// src/effects/tenantEffects.ts
|
|
1116
|
-
import { eventBus as
|
|
1318
|
+
import { eventBus as eventBus6, getStore as getStore3 } from "@hai3/state";
|
|
1117
1319
|
var TenantEvents = {
|
|
1118
1320
|
Changed: "app/tenant/changed",
|
|
1119
1321
|
Cleared: "app/tenant/cleared"
|
|
1120
1322
|
};
|
|
1121
1323
|
function initTenantEffects() {
|
|
1122
1324
|
const store = getStore3();
|
|
1123
|
-
const subChanged =
|
|
1325
|
+
const subChanged = eventBus6.on(TenantEvents.Changed, (payload) => {
|
|
1124
1326
|
store.dispatch(setTenant(payload.tenant));
|
|
1125
1327
|
});
|
|
1126
|
-
const subCleared =
|
|
1328
|
+
const subCleared = eventBus6.on(TenantEvents.Cleared, () => {
|
|
1127
1329
|
store.dispatch(clearTenant());
|
|
1128
1330
|
});
|
|
1129
1331
|
return () => {
|
|
@@ -1132,20 +1334,44 @@ function initTenantEffects() {
|
|
|
1132
1334
|
};
|
|
1133
1335
|
}
|
|
1134
1336
|
function changeTenant(tenant) {
|
|
1135
|
-
|
|
1337
|
+
eventBus6.emit(TenantEvents.Changed, { tenant });
|
|
1136
1338
|
}
|
|
1137
1339
|
function clearTenantAction() {
|
|
1138
|
-
|
|
1340
|
+
eventBus6.emit(TenantEvents.Cleared, {});
|
|
1139
1341
|
}
|
|
1140
1342
|
function setTenantLoadingState(loading) {
|
|
1141
1343
|
getStore3().dispatch(setTenantLoading(loading));
|
|
1142
1344
|
}
|
|
1143
1345
|
|
|
1144
1346
|
// src/index.ts
|
|
1145
|
-
import {
|
|
1347
|
+
import {
|
|
1348
|
+
apiRegistry as apiRegistry3,
|
|
1349
|
+
BaseApiService,
|
|
1350
|
+
RestProtocol,
|
|
1351
|
+
SseProtocol,
|
|
1352
|
+
RestMockPlugin,
|
|
1353
|
+
SseMockPlugin,
|
|
1354
|
+
MockEventSource,
|
|
1355
|
+
ApiPluginBase,
|
|
1356
|
+
ApiPlugin,
|
|
1357
|
+
ApiProtocol,
|
|
1358
|
+
RestPlugin,
|
|
1359
|
+
RestPluginWithConfig,
|
|
1360
|
+
SsePlugin,
|
|
1361
|
+
SsePluginWithConfig,
|
|
1362
|
+
isShortCircuit,
|
|
1363
|
+
isRestShortCircuit,
|
|
1364
|
+
isSseShortCircuit,
|
|
1365
|
+
MOCK_PLUGIN,
|
|
1366
|
+
isMockPlugin as isMockPlugin2
|
|
1367
|
+
} from "@hai3/api";
|
|
1146
1368
|
import { i18nRegistry as i18nRegistry2, I18nRegistryImpl, createI18nRegistry, Language as Language2, SUPPORTED_LANGUAGES, getLanguageMetadata, TextDirection, LanguageDisplayMode } from "@hai3/i18n";
|
|
1147
1369
|
import { I18nRegistryImpl as I18nRegistryImpl2 } from "@hai3/i18n";
|
|
1148
1370
|
|
|
1371
|
+
// src/compat.ts
|
|
1372
|
+
import { screensetRegistry as screensetRegistry2 } from "@hai3/screensets";
|
|
1373
|
+
var ACCOUNTS_DOMAIN = "accounts";
|
|
1374
|
+
|
|
1149
1375
|
// src/migration.ts
|
|
1150
1376
|
var STATE_PATH_MAPPING = {
|
|
1151
1377
|
// App state (moved to app slice)
|
|
@@ -1199,9 +1425,11 @@ function hasLegacyUicoreState(state) {
|
|
|
1199
1425
|
function hasNewLayoutState(state) {
|
|
1200
1426
|
return typeof state === "object" && state !== null && "layout" in state && typeof state.layout === "object";
|
|
1201
1427
|
}
|
|
1202
|
-
var legacySelectors = {};
|
|
1203
1428
|
export {
|
|
1204
1429
|
ACCOUNTS_DOMAIN,
|
|
1430
|
+
ApiPlugin,
|
|
1431
|
+
ApiPluginBase,
|
|
1432
|
+
ApiProtocol,
|
|
1205
1433
|
BaseApiService,
|
|
1206
1434
|
I18nRegistryImpl2 as I18nRegistry,
|
|
1207
1435
|
I18nRegistryImpl,
|
|
@@ -1209,11 +1437,19 @@ export {
|
|
|
1209
1437
|
Language2 as Language,
|
|
1210
1438
|
LanguageDisplayMode,
|
|
1211
1439
|
LayoutDomain,
|
|
1212
|
-
|
|
1440
|
+
MOCK_PLUGIN,
|
|
1441
|
+
MockEventSource,
|
|
1442
|
+
MockEvents,
|
|
1443
|
+
RestMockPlugin,
|
|
1444
|
+
RestPlugin,
|
|
1445
|
+
RestPluginWithConfig,
|
|
1213
1446
|
RestProtocol,
|
|
1214
1447
|
STATE_PATH_MAPPING,
|
|
1215
1448
|
SUPPORTED_LANGUAGES,
|
|
1216
1449
|
ScreensetCategory,
|
|
1450
|
+
SseMockPlugin,
|
|
1451
|
+
SsePlugin,
|
|
1452
|
+
SsePluginWithConfig,
|
|
1217
1453
|
SseProtocol,
|
|
1218
1454
|
TENANT_SLICE_NAME,
|
|
1219
1455
|
TenantEvents,
|
|
@@ -1233,12 +1469,11 @@ export {
|
|
|
1233
1469
|
createLegacySelector,
|
|
1234
1470
|
createRouteRegistry,
|
|
1235
1471
|
createScreensetRegistry,
|
|
1236
|
-
|
|
1472
|
+
createSlice10 as createSlice,
|
|
1237
1473
|
createStore,
|
|
1238
1474
|
createThemeRegistry,
|
|
1239
1475
|
effects,
|
|
1240
|
-
|
|
1241
|
-
fetchCurrentUser,
|
|
1476
|
+
eventBus7 as eventBus,
|
|
1242
1477
|
footerActions,
|
|
1243
1478
|
footerSlice,
|
|
1244
1479
|
full,
|
|
@@ -1254,17 +1489,23 @@ export {
|
|
|
1254
1489
|
hideOverlay,
|
|
1255
1490
|
i18n,
|
|
1256
1491
|
i18nRegistry2 as i18nRegistry,
|
|
1492
|
+
initMockEffects,
|
|
1257
1493
|
initTenantEffects,
|
|
1258
1494
|
isDeprecationWarningsEnabled,
|
|
1495
|
+
isMockPlugin2 as isMockPlugin,
|
|
1496
|
+
isRestShortCircuit,
|
|
1497
|
+
isShortCircuit,
|
|
1498
|
+
isSseShortCircuit,
|
|
1259
1499
|
layout,
|
|
1260
1500
|
layoutDomainReducers,
|
|
1261
1501
|
layoutReducer,
|
|
1262
|
-
legacySelectors,
|
|
1263
1502
|
menuActions,
|
|
1264
1503
|
menuSlice,
|
|
1265
1504
|
minimal,
|
|
1505
|
+
mock,
|
|
1506
|
+
mockActions,
|
|
1507
|
+
mockSlice,
|
|
1266
1508
|
navigateTo,
|
|
1267
|
-
navigateToScreen,
|
|
1268
1509
|
navigation,
|
|
1269
1510
|
openPopup,
|
|
1270
1511
|
overlayActions,
|
|
@@ -1273,11 +1514,10 @@ export {
|
|
|
1273
1514
|
popupSlice,
|
|
1274
1515
|
presets,
|
|
1275
1516
|
registerSlice2 as registerSlice,
|
|
1276
|
-
routeRegistry,
|
|
1277
1517
|
routing,
|
|
1278
1518
|
screenActions,
|
|
1279
1519
|
screenSlice,
|
|
1280
|
-
screensetRegistry,
|
|
1520
|
+
screensetRegistry2 as screensetRegistry,
|
|
1281
1521
|
screensets,
|
|
1282
1522
|
setActiveScreen,
|
|
1283
1523
|
setDeprecationWarnings,
|
|
@@ -1288,6 +1528,7 @@ export {
|
|
|
1288
1528
|
setMenuConfig,
|
|
1289
1529
|
setMenuItems,
|
|
1290
1530
|
setMenuVisible,
|
|
1531
|
+
setMockEnabled,
|
|
1291
1532
|
setOverlayVisible,
|
|
1292
1533
|
setScreenLoading,
|
|
1293
1534
|
setSidebarCollapsed,
|
|
@@ -1307,9 +1548,9 @@ export {
|
|
|
1307
1548
|
tenantActions,
|
|
1308
1549
|
tenantSlice_default as tenantReducer,
|
|
1309
1550
|
tenantSlice,
|
|
1310
|
-
themeRegistry,
|
|
1311
1551
|
themes,
|
|
1312
1552
|
toggleMenu,
|
|
1553
|
+
toggleMockMode,
|
|
1313
1554
|
toggleSidebar
|
|
1314
1555
|
};
|
|
1315
1556
|
//# sourceMappingURL=index.js.map
|