@hai3/framework 0.2.0-alpha.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/dist/index.js ADDED
@@ -0,0 +1,1264 @@
1
+ // src/createHAI3.ts
2
+ import { getStore, registerSlice } from "@hai3/state";
3
+ import { apiRegistry } from "@hai3/api";
4
+ function isPluginFactory(value) {
5
+ return typeof value === "function";
6
+ }
7
+ function resolvePlugin(plugin) {
8
+ return isPluginFactory(plugin) ? plugin() : plugin;
9
+ }
10
+ var HAI3AppBuilderImpl = class {
11
+ plugins = [];
12
+ config;
13
+ constructor(config = {}) {
14
+ this.config = {
15
+ name: "HAI3 App",
16
+ devMode: false,
17
+ strictMode: false,
18
+ ...config
19
+ };
20
+ }
21
+ /**
22
+ * Add a plugin to the application.
23
+ * Also accepts an array of plugins (for preset support).
24
+ */
25
+ use(plugin) {
26
+ if (Array.isArray(plugin)) {
27
+ plugin.forEach((p) => this.use(p));
28
+ return this;
29
+ }
30
+ const resolved = resolvePlugin(plugin);
31
+ if (this.plugins.some((p) => p.name === resolved.name)) {
32
+ if (this.config.devMode) {
33
+ console.warn(
34
+ `Plugin "${resolved.name}" is already registered. Skipping duplicate.`
35
+ );
36
+ }
37
+ return this;
38
+ }
39
+ this.plugins.push(resolved);
40
+ return this;
41
+ }
42
+ /**
43
+ * Add multiple plugins at once.
44
+ */
45
+ useAll(plugins) {
46
+ plugins.forEach((plugin) => this.use(plugin));
47
+ return this;
48
+ }
49
+ /**
50
+ * Build the application.
51
+ */
52
+ build() {
53
+ const orderedPlugins = this.resolveDependencies();
54
+ orderedPlugins.forEach((plugin) => {
55
+ if (plugin.onRegister) {
56
+ plugin.onRegister(this, plugin._configType);
57
+ }
58
+ });
59
+ const aggregated = this.aggregateProvides(orderedPlugins);
60
+ const store = this.createStoreWithSlices(aggregated.slices);
61
+ aggregated.effects.forEach((initEffect) => {
62
+ initEffect(store.dispatch);
63
+ });
64
+ const app = {
65
+ config: this.config,
66
+ store,
67
+ screensetRegistry: aggregated.registries.screensetRegistry,
68
+ themeRegistry: aggregated.registries.themeRegistry,
69
+ routeRegistry: aggregated.registries.routeRegistry,
70
+ apiRegistry,
71
+ i18nRegistry: aggregated.registries.i18nRegistry,
72
+ actions: aggregated.actions,
73
+ destroy: () => this.destroyApp(orderedPlugins, app)
74
+ };
75
+ orderedPlugins.forEach((plugin) => {
76
+ if (plugin.onInit) {
77
+ plugin.onInit(app);
78
+ }
79
+ });
80
+ return app;
81
+ }
82
+ /**
83
+ * Resolve plugin dependencies using topological sort.
84
+ */
85
+ resolveDependencies() {
86
+ const resolved = [];
87
+ const visited = /* @__PURE__ */ new Set();
88
+ const visiting = /* @__PURE__ */ new Set();
89
+ const visit = (plugin) => {
90
+ if (visited.has(plugin.name)) return;
91
+ if (visiting.has(plugin.name)) {
92
+ throw new Error(
93
+ `Circular dependency detected: ${plugin.name} depends on itself or creates a cycle.`
94
+ );
95
+ }
96
+ visiting.add(plugin.name);
97
+ if (plugin.dependencies) {
98
+ for (const depName of plugin.dependencies) {
99
+ const dep = this.plugins.find((p) => p.name === depName);
100
+ if (!dep) {
101
+ if (this.config.strictMode) {
102
+ throw new Error(
103
+ `Plugin "${plugin.name}" requires "${depName}" but it is not registered.
104
+ Add the missing plugin: .use(${depName}())`
105
+ );
106
+ } else {
107
+ console.warn(
108
+ `Plugin "${plugin.name}" requires "${depName}" but it is not registered. Some features may not work correctly.`
109
+ );
110
+ continue;
111
+ }
112
+ }
113
+ visit(dep);
114
+ }
115
+ }
116
+ visiting.delete(plugin.name);
117
+ visited.add(plugin.name);
118
+ resolved.push(plugin);
119
+ };
120
+ this.plugins.forEach(visit);
121
+ return resolved;
122
+ }
123
+ /**
124
+ * Aggregate all provides from plugins.
125
+ */
126
+ aggregateProvides(plugins) {
127
+ const registries = {};
128
+ const slices = [];
129
+ const effects2 = [];
130
+ const actions = {};
131
+ plugins.forEach((plugin) => {
132
+ if (!plugin.provides) return;
133
+ if (plugin.provides.registries) {
134
+ Object.assign(registries, plugin.provides.registries);
135
+ }
136
+ if (plugin.provides.slices) {
137
+ slices.push(...plugin.provides.slices);
138
+ }
139
+ if (plugin.provides.effects) {
140
+ effects2.push(...plugin.provides.effects);
141
+ }
142
+ if (plugin.provides.actions) {
143
+ Object.assign(actions, plugin.provides.actions);
144
+ }
145
+ });
146
+ return { registries, slices, effects: effects2, actions };
147
+ }
148
+ /**
149
+ * Create store with all aggregated slices.
150
+ *
151
+ * IMPORTANT: This method supports the screenset self-registration pattern.
152
+ * Screensets call registerSlice() as module side effects when imported,
153
+ * which may auto-create a store before createHAI3App() is called.
154
+ *
155
+ * This method:
156
+ * 1. Uses the existing store if one was auto-created by screensets
157
+ * 2. Registers framework slices to the existing store
158
+ * 3. Returns the unified store for HAI3App
159
+ */
160
+ createStoreWithSlices(slices) {
161
+ const store = getStore();
162
+ slices.forEach((slice9) => {
163
+ registerSlice(slice9);
164
+ });
165
+ return store;
166
+ }
167
+ /**
168
+ * Destroy the app and cleanup resources.
169
+ */
170
+ destroyApp(plugins, app) {
171
+ [...plugins].reverse().forEach((plugin) => {
172
+ if (plugin.onDestroy) {
173
+ plugin.onDestroy(app);
174
+ }
175
+ });
176
+ }
177
+ };
178
+ function createHAI3(config) {
179
+ return new HAI3AppBuilderImpl(config);
180
+ }
181
+
182
+ // src/plugins/screensets.ts
183
+ import { screensetRegistry as sdkScreensetRegistry } from "@hai3/screensets";
184
+
185
+ // src/slices/index.ts
186
+ import { combineReducers } from "@reduxjs/toolkit";
187
+
188
+ // src/slices/headerSlice.ts
189
+ import { createSlice } from "@hai3/state";
190
+ var SLICE_KEY = "layout/header";
191
+ var initialState = {
192
+ user: null,
193
+ loading: false
194
+ };
195
+ var { slice, setUser, setLoading, clearUser } = createSlice({
196
+ name: SLICE_KEY,
197
+ initialState,
198
+ reducers: {
199
+ setUser: (state, action) => {
200
+ state.user = action.payload;
201
+ state.loading = false;
202
+ },
203
+ setLoading: (state, action) => {
204
+ state.loading = action.payload;
205
+ },
206
+ clearUser: (state) => {
207
+ state.user = null;
208
+ state.loading = false;
209
+ }
210
+ }
211
+ });
212
+ var headerSlice = slice;
213
+ var headerActions = { setUser, setLoading, clearUser };
214
+ var headerSlice_default = slice.reducer;
215
+
216
+ // src/slices/footerSlice.ts
217
+ import { createSlice as createSlice2 } from "@hai3/state";
218
+ var SLICE_KEY2 = "layout/footer";
219
+ var initialState2 = {
220
+ screensetOptions: [],
221
+ visible: true
222
+ };
223
+ var { slice: slice2, setFooterVisible, setFooterConfig, ...restActions } = createSlice2({
224
+ name: SLICE_KEY2,
225
+ initialState: initialState2,
226
+ reducers: {
227
+ setFooterVisible: (state, action) => {
228
+ state.visible = action.payload;
229
+ },
230
+ setFooterConfig: (state, action) => {
231
+ return { ...state, ...action.payload };
232
+ }
233
+ }
234
+ });
235
+ var footerSlice = slice2;
236
+ var footerActions = { setFooterVisible, setFooterConfig, ...restActions };
237
+ var footerSlice_default = slice2.reducer;
238
+
239
+ // src/slices/menuSlice.ts
240
+ import { createSlice as createSlice3 } from "@hai3/state";
241
+ var SLICE_KEY3 = "layout/menu";
242
+ var initialState3 = {
243
+ collapsed: false,
244
+ items: [],
245
+ visible: true
246
+ };
247
+ var { slice: slice3, toggleMenu, setMenuCollapsed, setMenuItems, setMenuVisible, setMenuConfig } = createSlice3({
248
+ name: SLICE_KEY3,
249
+ initialState: initialState3,
250
+ reducers: {
251
+ toggleMenu: (state) => {
252
+ state.collapsed = !state.collapsed;
253
+ },
254
+ setMenuCollapsed: (state, action) => {
255
+ state.collapsed = action.payload;
256
+ },
257
+ setMenuItems: (state, action) => {
258
+ state.items = action.payload;
259
+ },
260
+ setMenuVisible: (state, action) => {
261
+ state.visible = action.payload;
262
+ },
263
+ setMenuConfig: (state, action) => {
264
+ return { ...state, ...action.payload };
265
+ }
266
+ }
267
+ });
268
+ var menuSlice = slice3;
269
+ var menuActions = { toggleMenu, setMenuCollapsed, setMenuItems, setMenuVisible, setMenuConfig };
270
+ var menuSlice_default = slice3.reducer;
271
+
272
+ // src/slices/sidebarSlice.ts
273
+ import { createSlice as createSlice4 } from "@hai3/state";
274
+ var SLICE_KEY4 = "layout/sidebar";
275
+ var initialState4 = {
276
+ collapsed: false,
277
+ position: "left",
278
+ title: null,
279
+ content: null,
280
+ visible: false,
281
+ width: 256
282
+ };
283
+ var {
284
+ slice: slice4,
285
+ toggleSidebar,
286
+ setSidebarCollapsed,
287
+ setSidebarPosition,
288
+ setSidebarTitle,
289
+ setSidebarContent,
290
+ setSidebarVisible,
291
+ setSidebarWidth,
292
+ setSidebarConfig
293
+ } = createSlice4({
294
+ name: SLICE_KEY4,
295
+ initialState: initialState4,
296
+ reducers: {
297
+ toggleSidebar: (state) => {
298
+ state.collapsed = !state.collapsed;
299
+ },
300
+ setSidebarCollapsed: (state, action) => {
301
+ state.collapsed = action.payload;
302
+ },
303
+ setSidebarPosition: (state, action) => {
304
+ state.position = action.payload;
305
+ },
306
+ setSidebarTitle: (state, action) => {
307
+ state.title = action.payload;
308
+ },
309
+ setSidebarContent: (state, action) => {
310
+ state.content = action.payload;
311
+ },
312
+ setSidebarVisible: (state, action) => {
313
+ state.visible = action.payload;
314
+ },
315
+ setSidebarWidth: (state, action) => {
316
+ state.width = action.payload;
317
+ },
318
+ setSidebarConfig: (state, action) => {
319
+ return { ...state, ...action.payload };
320
+ }
321
+ }
322
+ });
323
+ var sidebarSlice = slice4;
324
+ var sidebarActions = {
325
+ toggleSidebar,
326
+ setSidebarCollapsed,
327
+ setSidebarPosition,
328
+ setSidebarTitle,
329
+ setSidebarContent,
330
+ setSidebarVisible,
331
+ setSidebarWidth,
332
+ setSidebarConfig
333
+ };
334
+ var sidebarSlice_default = slice4.reducer;
335
+
336
+ // src/slices/screenSlice.ts
337
+ import { createSlice as createSlice5 } from "@hai3/state";
338
+ var SLICE_KEY5 = "layout/screen";
339
+ var initialState5 = {
340
+ activeScreen: null,
341
+ loading: false
342
+ };
343
+ var { slice: slice5, setActiveScreen, setScreenLoading, navigateTo, clearActiveScreen } = createSlice5({
344
+ name: SLICE_KEY5,
345
+ initialState: initialState5,
346
+ reducers: {
347
+ setActiveScreen: (state, action) => {
348
+ state.activeScreen = action.payload;
349
+ },
350
+ setScreenLoading: (state, action) => {
351
+ state.loading = action.payload;
352
+ },
353
+ navigateTo: (state, action) => {
354
+ state.activeScreen = action.payload;
355
+ },
356
+ clearActiveScreen: (state) => {
357
+ state.activeScreen = null;
358
+ }
359
+ }
360
+ });
361
+ var screenSlice = slice5;
362
+ var screenActions = { setActiveScreen, setScreenLoading, navigateTo, clearActiveScreen };
363
+ var screenSlice_default = slice5.reducer;
364
+
365
+ // src/slices/popupSlice.ts
366
+ import { createSlice as createSlice6 } from "@hai3/state";
367
+ var SLICE_KEY6 = "layout/popup";
368
+ var initialState6 = {
369
+ stack: []
370
+ };
371
+ var { slice: slice6, openPopup, closePopup, closeTopPopup, closeAllPopups } = createSlice6({
372
+ name: SLICE_KEY6,
373
+ initialState: initialState6,
374
+ reducers: {
375
+ openPopup: (state, action) => {
376
+ const zIndex = 1e3 + state.stack.length * 10;
377
+ state.stack.push({ ...action.payload, zIndex });
378
+ },
379
+ closePopup: (state, action) => {
380
+ state.stack = state.stack.filter((popup) => popup.id !== action.payload);
381
+ },
382
+ closeTopPopup: (state) => {
383
+ state.stack.pop();
384
+ },
385
+ closeAllPopups: (state) => {
386
+ state.stack = [];
387
+ }
388
+ }
389
+ });
390
+ var popupSlice = slice6;
391
+ var popupActions = { openPopup, closePopup, closeTopPopup, closeAllPopups };
392
+ var popupSlice_default = slice6.reducer;
393
+
394
+ // src/slices/overlaySlice.ts
395
+ import { createSlice as createSlice7 } from "@hai3/state";
396
+ var SLICE_KEY7 = "layout/overlay";
397
+ var initialState7 = {
398
+ visible: false
399
+ };
400
+ var { slice: slice7, showOverlay, hideOverlay, setOverlayVisible } = createSlice7({
401
+ name: SLICE_KEY7,
402
+ initialState: initialState7,
403
+ reducers: {
404
+ showOverlay: (state) => {
405
+ state.visible = true;
406
+ },
407
+ hideOverlay: (state) => {
408
+ state.visible = false;
409
+ },
410
+ setOverlayVisible: (state, action) => {
411
+ state.visible = action.payload;
412
+ }
413
+ }
414
+ });
415
+ var overlaySlice = slice7;
416
+ var overlayActions = { showOverlay, hideOverlay, setOverlayVisible };
417
+ var overlaySlice_default = slice7.reducer;
418
+
419
+ // src/slices/tenantSlice.ts
420
+ import { createSlice as createSlice8 } from "@hai3/state";
421
+ var SLICE_KEY8 = "app/tenant";
422
+ var initialState8 = {
423
+ tenant: null,
424
+ loading: false
425
+ };
426
+ var { slice: slice8, setTenant, setTenantLoading, clearTenant } = createSlice8({
427
+ name: SLICE_KEY8,
428
+ initialState: initialState8,
429
+ reducers: {
430
+ setTenant: (state, action) => {
431
+ state.tenant = action.payload;
432
+ state.loading = false;
433
+ },
434
+ setTenantLoading: (state, action) => {
435
+ state.loading = action.payload;
436
+ },
437
+ clearTenant: (state) => {
438
+ state.tenant = null;
439
+ state.loading = false;
440
+ }
441
+ }
442
+ });
443
+ var tenantSlice = slice8;
444
+ var tenantActions = { setTenant, setTenantLoading, clearTenant };
445
+ var tenantSlice_default = slice8.reducer;
446
+
447
+ // src/slices/index.ts
448
+ var LAYOUT_SLICE_NAME = "layout";
449
+ var TENANT_SLICE_NAME = "app/tenant";
450
+ var layoutDomainReducers = {
451
+ header: headerSlice_default,
452
+ footer: footerSlice_default,
453
+ menu: menuSlice_default,
454
+ sidebar: sidebarSlice_default,
455
+ screen: screenSlice_default,
456
+ popup: popupSlice_default,
457
+ overlay: overlaySlice_default
458
+ };
459
+ var layoutReducer = combineReducers(layoutDomainReducers);
460
+
461
+ // src/plugins/screensets.ts
462
+ var screenSlice2 = screenSlice;
463
+ var screenActions2 = screenActions;
464
+ function screensets(config) {
465
+ const screensetRegistry3 = sdkScreensetRegistry;
466
+ return {
467
+ name: "screensets",
468
+ dependencies: [],
469
+ provides: {
470
+ registries: {
471
+ screensetRegistry: screensetRegistry3
472
+ },
473
+ slices: [screenSlice2],
474
+ actions: {
475
+ setActiveScreen: screenActions2.navigateTo,
476
+ setScreenLoading: screenActions2.setScreenLoading
477
+ }
478
+ },
479
+ onInit() {
480
+ if (config?.autoDiscover) {
481
+ console.log(
482
+ "[HAI3] Auto-discover is enabled. Screensets should be registered via screensetRegistry.register() in your app."
483
+ );
484
+ }
485
+ }
486
+ };
487
+ }
488
+
489
+ // src/plugins/themes.ts
490
+ import { eventBus } from "@hai3/state";
491
+
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
+ // src/registries/themeRegistry.ts
498
+ function createThemeRegistry() {
499
+ const themes2 = /* @__PURE__ */ new Map();
500
+ const legacyThemes = /* @__PURE__ */ new Map();
501
+ let currentThemeId = null;
502
+ let customApplyFn = null;
503
+ const subscribers = /* @__PURE__ */ new Set();
504
+ let version = 0;
505
+ function notifySubscribers() {
506
+ version++;
507
+ subscribers.forEach((callback) => callback());
508
+ }
509
+ function applyCSSVariables(config) {
510
+ if (typeof document === "undefined") return;
511
+ const root = document.documentElement;
512
+ Object.entries(config.variables).forEach(([key, value]) => {
513
+ root.style.setProperty(key, value);
514
+ });
515
+ }
516
+ return {
517
+ /**
518
+ * Register a theme.
519
+ * Supports both new API (config only) and legacy API (id + theme).
520
+ */
521
+ register(configOrId, legacyTheme) {
522
+ if (typeof configOrId === "string") {
523
+ const id = configOrId;
524
+ if (!legacyTheme) {
525
+ console.warn(`register() called with ID "${id}" but no theme object. Skipping.`);
526
+ return;
527
+ }
528
+ if (themes2.has(id)) {
529
+ console.warn(`Theme "${id}" is already registered. Skipping.`);
530
+ return;
531
+ }
532
+ legacyThemes.set(id, legacyTheme);
533
+ let themeName = id;
534
+ if (legacyTheme && typeof legacyTheme === "object" && "name" in legacyTheme) {
535
+ const nameValue = legacyTheme.name;
536
+ if (typeof nameValue === "string") {
537
+ themeName = nameValue;
538
+ }
539
+ }
540
+ const config2 = {
541
+ id,
542
+ name: themeName,
543
+ variables: {}
544
+ // Legacy themes use custom apply function
545
+ };
546
+ themes2.set(id, config2);
547
+ return;
548
+ }
549
+ const config = configOrId;
550
+ if (themes2.has(config.id)) {
551
+ console.warn(`Theme "${config.id}" is already registered. Skipping.`);
552
+ return;
553
+ }
554
+ themes2.set(config.id, config);
555
+ if (config.default && currentThemeId === null) {
556
+ this.apply(config.id);
557
+ }
558
+ },
559
+ /**
560
+ * Set the apply function (legacy API).
561
+ */
562
+ setApplyFunction(applyFn) {
563
+ customApplyFn = applyFn;
564
+ },
565
+ /**
566
+ * Get theme by ID.
567
+ */
568
+ get(id) {
569
+ return themes2.get(id);
570
+ },
571
+ /**
572
+ * Get all themes.
573
+ */
574
+ getAll() {
575
+ return Array.from(themes2.values());
576
+ },
577
+ /**
578
+ * Apply a theme.
579
+ */
580
+ apply(id) {
581
+ const config = themes2.get(id);
582
+ if (!config) {
583
+ console.warn(`Theme "${id}" not found. Cannot apply.`);
584
+ return;
585
+ }
586
+ const legacyTheme = legacyThemes.get(id);
587
+ if (legacyTheme && customApplyFn) {
588
+ customApplyFn(legacyTheme, id);
589
+ } else if (config.variables && Object.keys(config.variables).length > 0) {
590
+ applyCSSVariables(config);
591
+ }
592
+ currentThemeId = id;
593
+ notifySubscribers();
594
+ },
595
+ /**
596
+ * Get current theme.
597
+ */
598
+ getCurrent() {
599
+ return currentThemeId ? themes2.get(currentThemeId) : void 0;
600
+ },
601
+ /**
602
+ * Subscribe to theme changes.
603
+ * Returns unsubscribe function.
604
+ */
605
+ subscribe(callback) {
606
+ subscribers.add(callback);
607
+ return () => {
608
+ subscribers.delete(callback);
609
+ };
610
+ },
611
+ /**
612
+ * Get current version number.
613
+ * Used by React for re-rendering.
614
+ */
615
+ getVersion() {
616
+ return version;
617
+ }
618
+ };
619
+ }
620
+
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
+ // src/plugins/themes.ts
721
+ function changeTheme(payload) {
722
+ eventBus.emit("theme/changed", payload);
723
+ }
724
+ function themes() {
725
+ const themeRegistry2 = themeRegistry;
726
+ return {
727
+ name: "themes",
728
+ dependencies: [],
729
+ provides: {
730
+ registries: {
731
+ themeRegistry: themeRegistry2
732
+ },
733
+ actions: {
734
+ changeTheme
735
+ }
736
+ },
737
+ onInit(_app) {
738
+ eventBus.on("theme/changed", (payload) => {
739
+ themeRegistry2.apply(payload.themeId);
740
+ });
741
+ const themes2 = themeRegistry2.getAll();
742
+ if (themes2.length > 0) {
743
+ themeRegistry2.apply(themes2[0].id);
744
+ }
745
+ }
746
+ };
747
+ }
748
+
749
+ // src/plugins/layout.ts
750
+ import { eventBus as eventBus2 } from "@hai3/state";
751
+ var headerSlice2 = headerSlice;
752
+ var footerSlice2 = footerSlice;
753
+ var menuSlice2 = menuSlice;
754
+ var sidebarSlice2 = sidebarSlice;
755
+ var popupSlice2 = popupSlice;
756
+ var overlaySlice2 = overlaySlice;
757
+ var headerActions2 = headerActions;
758
+ var footerActions2 = footerActions;
759
+ var menuActions2 = menuActions;
760
+ var sidebarActions2 = sidebarActions;
761
+ var popupActions2 = popupActions;
762
+ var overlayActions2 = overlayActions;
763
+ function showPopup(payload) {
764
+ eventBus2.emit("layout/popup/requested", payload);
765
+ }
766
+ function hidePopup() {
767
+ eventBus2.emit("layout/popup/hidden");
768
+ }
769
+ function showOverlay2(payload) {
770
+ eventBus2.emit("layout/overlay/requested", payload);
771
+ }
772
+ function hideOverlay2() {
773
+ eventBus2.emit("layout/overlay/hidden");
774
+ }
775
+ function toggleMenuCollapsed(payload) {
776
+ eventBus2.emit("layout/menu/collapsed", payload);
777
+ }
778
+ function toggleSidebarCollapsed(payload) {
779
+ eventBus2.emit("layout/sidebar/collapsed", payload);
780
+ }
781
+ function layout() {
782
+ return {
783
+ name: "layout",
784
+ dependencies: ["screensets"],
785
+ provides: {
786
+ slices: [
787
+ headerSlice2,
788
+ footerSlice2,
789
+ menuSlice2,
790
+ sidebarSlice2,
791
+ popupSlice2,
792
+ overlaySlice2
793
+ ],
794
+ actions: {
795
+ showPopup,
796
+ hidePopup,
797
+ showOverlay: showOverlay2,
798
+ hideOverlay: hideOverlay2,
799
+ toggleMenuCollapsed,
800
+ toggleSidebarCollapsed,
801
+ // Direct slice actions for backward compatibility
802
+ setHeaderVisible: headerActions2.setVisible,
803
+ setFooterVisible: footerActions2.setVisible,
804
+ setMenuCollapsed: menuActions2.setCollapsed,
805
+ setSidebarCollapsed: sidebarActions2.setCollapsed
806
+ }
807
+ },
808
+ onInit(app) {
809
+ const dispatch = app.store.dispatch;
810
+ eventBus2.on("layout/popup/requested", (payload) => {
811
+ dispatch(popupActions2.open({
812
+ id: payload.id,
813
+ title: payload.title,
814
+ content: payload.content,
815
+ size: payload.size
816
+ }));
817
+ });
818
+ eventBus2.on("layout/popup/hidden", () => {
819
+ dispatch(popupActions2.close());
820
+ });
821
+ eventBus2.on("layout/overlay/requested", (payload) => {
822
+ dispatch(overlayActions2.show({ id: payload.id }));
823
+ });
824
+ eventBus2.on("layout/overlay/hidden", () => {
825
+ dispatch(overlayActions2.hide());
826
+ });
827
+ eventBus2.on("layout/menu/collapsed", (payload) => {
828
+ dispatch(menuActions2.setCollapsed(payload.collapsed));
829
+ });
830
+ eventBus2.on("layout/sidebar/collapsed", (payload) => {
831
+ dispatch(sidebarActions2.setCollapsed(payload.collapsed));
832
+ });
833
+ }
834
+ };
835
+ }
836
+
837
+ // src/plugins/navigation.ts
838
+ import { eventBus as eventBus3 } from "@hai3/state";
839
+ import { i18nRegistry } from "@hai3/i18n";
840
+ var screenActions4 = screenActions;
841
+ var menuActions3 = menuActions;
842
+ function buildMenuItems(screenset) {
843
+ return screenset.menu.map((item) => ({
844
+ id: item.menuItem.screenId ?? item.menuItem.id,
845
+ label: item.menuItem.label,
846
+ icon: item.menuItem.icon
847
+ }));
848
+ }
849
+ function navigateToScreen2(payload) {
850
+ eventBus3.emit("navigation/screen/navigated", payload);
851
+ }
852
+ function navigateToScreenset(payload) {
853
+ eventBus3.emit("navigation/screenset/navigated", payload);
854
+ }
855
+ function navigation() {
856
+ return {
857
+ name: "navigation",
858
+ dependencies: ["screensets"],
859
+ provides: {
860
+ actions: {
861
+ navigateToScreen: navigateToScreen2,
862
+ navigateToScreenset
863
+ }
864
+ },
865
+ onInit(app) {
866
+ const dispatch = app.store.dispatch;
867
+ let currentScreensetId = null;
868
+ async function loadScreensetTranslations(screensetId, language) {
869
+ await i18nRegistry.loadScreensetTranslations(screensetId, language);
870
+ }
871
+ function updateMenuForScreenset(screensetId) {
872
+ if (screensetId === currentScreensetId) return;
873
+ const screenset = app.screensetRegistry.get(screensetId);
874
+ if (!screenset) return;
875
+ currentScreensetId = screensetId;
876
+ loadScreensetTranslations(screensetId).catch((err) => {
877
+ console.warn(`[HAI3] Failed to load translations for screenset ${screensetId}:`, err);
878
+ });
879
+ const menuItems = buildMenuItems(screenset);
880
+ dispatch(menuActions3.setMenuItems(menuItems));
881
+ }
882
+ eventBus3.on("navigation/screen/navigated", (payload) => {
883
+ if (app.routeRegistry && !app.routeRegistry.hasScreen(payload.screensetId, payload.screenId)) {
884
+ console.warn(
885
+ `Screen "${payload.screenId}" in screenset "${payload.screensetId}" not found.`
886
+ );
887
+ return;
888
+ }
889
+ updateMenuForScreenset(payload.screensetId);
890
+ dispatch(screenActions4.navigateTo(payload.screenId));
891
+ if (typeof window !== "undefined") {
892
+ const url = `/${payload.screenId}`;
893
+ window.history.pushState(null, "", url);
894
+ }
895
+ });
896
+ eventBus3.on("navigation/screenset/navigated", (payload) => {
897
+ const screenset = app.screensetRegistry.get(payload.screensetId);
898
+ if (!screenset) {
899
+ console.warn(`Screenset "${payload.screensetId}" not found.`);
900
+ return;
901
+ }
902
+ navigateToScreen2({
903
+ screensetId: payload.screensetId,
904
+ screenId: screenset.defaultScreen
905
+ });
906
+ });
907
+ let lastLoadedLanguage = null;
908
+ i18nRegistry.subscribe(() => {
909
+ const currentLanguage = i18nRegistry.getLanguage();
910
+ if (!currentLanguage || currentLanguage === lastLoadedLanguage) return;
911
+ if (!currentScreensetId) return;
912
+ const screenset = app.screensetRegistry.get(currentScreensetId);
913
+ if (!screenset) return;
914
+ lastLoadedLanguage = currentLanguage;
915
+ loadScreensetTranslations(currentScreensetId, currentLanguage).then(() => {
916
+ const menuItems = buildMenuItems(screenset);
917
+ dispatch(menuActions3.setMenuItems(menuItems));
918
+ }).catch((err) => {
919
+ console.warn(
920
+ `[HAI3] Failed to reload translations for screenset ${currentScreensetId}:`,
921
+ err
922
+ );
923
+ });
924
+ });
925
+ if (typeof window !== "undefined") {
926
+ window.addEventListener("popstate", () => {
927
+ const path2 = window.location.pathname;
928
+ const parts2 = path2.split("/").filter(Boolean);
929
+ if (parts2.length >= 1) {
930
+ const screenId = parts2[0];
931
+ const screensetId = app.routeRegistry?.getScreensetForScreen(screenId);
932
+ if (screensetId) {
933
+ updateMenuForScreenset(screensetId);
934
+ dispatch(screenActions4.navigateTo(screenId));
935
+ }
936
+ }
937
+ });
938
+ const path = window.location.pathname;
939
+ const parts = path.split("/").filter(Boolean);
940
+ const autoNavigate = app.config.autoNavigate !== false;
941
+ if (parts.length >= 1) {
942
+ const screenId = parts[0];
943
+ const screensetId = app.routeRegistry?.getScreensetForScreen(screenId);
944
+ if (screensetId) {
945
+ navigateToScreen2({ screensetId, screenId });
946
+ } else if (autoNavigate) {
947
+ const screensets2 = app.screensetRegistry.getAll();
948
+ if (screensets2.length > 0) {
949
+ navigateToScreenset({ screensetId: screensets2[0].id });
950
+ }
951
+ }
952
+ } else if (autoNavigate) {
953
+ const screensets2 = app.screensetRegistry.getAll();
954
+ if (screensets2.length > 0) {
955
+ navigateToScreenset({ screensetId: screensets2[0].id });
956
+ }
957
+ }
958
+ }
959
+ }
960
+ };
961
+ }
962
+
963
+ // src/plugins/routing.ts
964
+ function routing() {
965
+ return {
966
+ name: "routing",
967
+ dependencies: ["screensets"],
968
+ onRegister(_app) {
969
+ },
970
+ onInit(app) {
971
+ const routeRegistry2 = createRouteRegistry(app.screensetRegistry);
972
+ app.routeRegistry = routeRegistry2;
973
+ }
974
+ };
975
+ }
976
+
977
+ // src/plugins/i18n.ts
978
+ import { eventBus as eventBus4 } from "@hai3/state";
979
+ import { i18nRegistry as singletonI18nRegistry, Language } from "@hai3/i18n";
980
+ function setLanguage(payload) {
981
+ eventBus4.emit("i18n/language/changed", payload);
982
+ }
983
+ function i18n() {
984
+ const i18nRegistry3 = singletonI18nRegistry;
985
+ return {
986
+ name: "i18n",
987
+ dependencies: [],
988
+ provides: {
989
+ registries: {
990
+ i18nRegistry: i18nRegistry3
991
+ },
992
+ actions: {
993
+ setLanguage
994
+ }
995
+ },
996
+ onInit(_app) {
997
+ eventBus4.on("i18n/language/changed", async (payload) => {
998
+ await i18nRegistry3.setLanguage(payload.language);
999
+ });
1000
+ i18nRegistry3.setLanguage(Language.English).catch((err) => {
1001
+ console.warn("[HAI3] Failed to load initial translations:", err);
1002
+ });
1003
+ }
1004
+ };
1005
+ }
1006
+
1007
+ // src/plugins/effects.ts
1008
+ function effects() {
1009
+ return {
1010
+ name: "effects",
1011
+ dependencies: [],
1012
+ onInit(app) {
1013
+ if (app.config.devMode) {
1014
+ console.log("[HAI3] Effects plugin initialized");
1015
+ }
1016
+ }
1017
+ };
1018
+ }
1019
+
1020
+ // src/presets/index.ts
1021
+ function full() {
1022
+ return [
1023
+ effects(),
1024
+ screensets({ autoDiscover: true }),
1025
+ themes(),
1026
+ layout(),
1027
+ routing(),
1028
+ navigation(),
1029
+ i18n()
1030
+ ];
1031
+ }
1032
+ function minimal() {
1033
+ return [
1034
+ screensets({ autoDiscover: true }),
1035
+ themes()
1036
+ ];
1037
+ }
1038
+ function headless() {
1039
+ return [
1040
+ screensets()
1041
+ ];
1042
+ }
1043
+ var presets = {
1044
+ full,
1045
+ minimal,
1046
+ headless
1047
+ };
1048
+
1049
+ // src/createHAI3App.ts
1050
+ function createHAI3App(config) {
1051
+ return createHAI3(config).useAll(full()).build();
1052
+ }
1053
+
1054
+ // src/registries/index.ts
1055
+ import { createScreensetRegistry, screensetRegistry as screensetRegistry2 } from "@hai3/screensets";
1056
+
1057
+ // src/index.ts
1058
+ import { eventBus as eventBus6, createStore, getStore as getStore4, registerSlice as registerSlice2, hasSlice, createSlice as createSlice9 } from "@hai3/state";
1059
+ import {
1060
+ LayoutDomain,
1061
+ ScreensetCategory
1062
+ } from "@hai3/screensets";
1063
+
1064
+ // src/effects/tenantEffects.ts
1065
+ import { eventBus as eventBus5, getStore as getStore3 } from "@hai3/state";
1066
+ var TenantEvents = {
1067
+ Changed: "app/tenant/changed",
1068
+ Cleared: "app/tenant/cleared"
1069
+ };
1070
+ function initTenantEffects() {
1071
+ const store = getStore3();
1072
+ const subChanged = eventBus5.on(TenantEvents.Changed, (payload) => {
1073
+ store.dispatch(setTenant(payload.tenant));
1074
+ });
1075
+ const subCleared = eventBus5.on(TenantEvents.Cleared, () => {
1076
+ store.dispatch(clearTenant());
1077
+ });
1078
+ return () => {
1079
+ subChanged.unsubscribe();
1080
+ subCleared.unsubscribe();
1081
+ };
1082
+ }
1083
+ function changeTenant(tenant) {
1084
+ eventBus5.emit(TenantEvents.Changed, { tenant });
1085
+ }
1086
+ function clearTenantAction() {
1087
+ eventBus5.emit(TenantEvents.Cleared, {});
1088
+ }
1089
+ function setTenantLoadingState(loading) {
1090
+ getStore3().dispatch(setTenantLoading(loading));
1091
+ }
1092
+
1093
+ // src/index.ts
1094
+ import { apiRegistry as apiRegistry3, BaseApiService, RestProtocol, SseProtocol, MockPlugin } from "@hai3/api";
1095
+ import { i18nRegistry as i18nRegistry2, I18nRegistryImpl, createI18nRegistry, Language as Language2, SUPPORTED_LANGUAGES, getLanguageMetadata, TextDirection, LanguageDisplayMode } from "@hai3/i18n";
1096
+ import { I18nRegistryImpl as I18nRegistryImpl2 } from "@hai3/i18n";
1097
+
1098
+ // src/migration.ts
1099
+ var STATE_PATH_MAPPING = {
1100
+ // App state (moved to app slice)
1101
+ "uicore.app.user": "app.user",
1102
+ "uicore.app.tenant": "app.tenant",
1103
+ "uicore.app.language": "app.language",
1104
+ "uicore.app.translationsReady": "app.translationsReady",
1105
+ "uicore.app.loading": "app.loading",
1106
+ "uicore.app.error": "app.error",
1107
+ "uicore.app.useMockApi": "app.useMockApi",
1108
+ // Layout state (split into domains)
1109
+ "uicore.layout.theme": "app.theme",
1110
+ "uicore.layout.currentScreenset": "app.currentScreenset",
1111
+ "uicore.layout.selectedScreen": "layout.screen.activeScreen",
1112
+ // Domain states (moved to layout.*)
1113
+ "uicore.header": "layout.header",
1114
+ "uicore.footer": "layout.footer",
1115
+ "uicore.menu": "layout.menu",
1116
+ "uicore.sidebar": "layout.sidebar",
1117
+ "uicore.screen": "layout.screen",
1118
+ "uicore.popup": "layout.popup",
1119
+ "uicore.overlay": "layout.overlay"
1120
+ };
1121
+ var deprecationWarningsEnabled = true;
1122
+ function setDeprecationWarnings(enabled) {
1123
+ deprecationWarningsEnabled = enabled;
1124
+ }
1125
+ function isDeprecationWarningsEnabled() {
1126
+ return deprecationWarningsEnabled;
1127
+ }
1128
+ function createLegacySelector(legacyPath, newSelector, migrationHint) {
1129
+ let hasWarned = false;
1130
+ return (state) => {
1131
+ if (deprecationWarningsEnabled && !hasWarned && process.env.NODE_ENV === "development") {
1132
+ hasWarned = true;
1133
+ const newPath = STATE_PATH_MAPPING[legacyPath] ?? "unknown";
1134
+ const hint = migrationHint ?? `Use the new state path: ${newPath}`;
1135
+ console.warn(
1136
+ `[HAI3 Migration] Deprecated selector accessing "${legacyPath}". ${hint}`
1137
+ );
1138
+ }
1139
+ return newSelector(state);
1140
+ };
1141
+ }
1142
+ function getLayoutDomainState(state, domain) {
1143
+ return state.layout[domain];
1144
+ }
1145
+ function hasLegacyUicoreState(state) {
1146
+ return typeof state === "object" && state !== null && "uicore" in state && typeof state.uicore === "object";
1147
+ }
1148
+ function hasNewLayoutState(state) {
1149
+ return typeof state === "object" && state !== null && "layout" in state && typeof state.layout === "object";
1150
+ }
1151
+ var legacySelectors = {};
1152
+ export {
1153
+ ACCOUNTS_DOMAIN,
1154
+ BaseApiService,
1155
+ I18nRegistryImpl2 as I18nRegistry,
1156
+ I18nRegistryImpl,
1157
+ LAYOUT_SLICE_NAME,
1158
+ Language2 as Language,
1159
+ LanguageDisplayMode,
1160
+ LayoutDomain,
1161
+ MockPlugin,
1162
+ RestProtocol,
1163
+ STATE_PATH_MAPPING,
1164
+ SUPPORTED_LANGUAGES,
1165
+ ScreensetCategory,
1166
+ SseProtocol,
1167
+ TENANT_SLICE_NAME,
1168
+ TenantEvents,
1169
+ TextDirection,
1170
+ apiRegistry3 as apiRegistry,
1171
+ changeTenant,
1172
+ clearActiveScreen,
1173
+ clearTenant,
1174
+ clearTenantAction,
1175
+ clearUser,
1176
+ closeAllPopups,
1177
+ closePopup,
1178
+ closeTopPopup,
1179
+ createHAI3,
1180
+ createHAI3App,
1181
+ createI18nRegistry,
1182
+ createLegacySelector,
1183
+ createRouteRegistry,
1184
+ createScreensetRegistry,
1185
+ createSlice9 as createSlice,
1186
+ createStore,
1187
+ createThemeRegistry,
1188
+ effects,
1189
+ eventBus6 as eventBus,
1190
+ fetchCurrentUser,
1191
+ footerActions,
1192
+ footerSlice,
1193
+ full,
1194
+ getLanguageMetadata,
1195
+ getLayoutDomainState,
1196
+ getStore4 as getStore,
1197
+ hasLegacyUicoreState,
1198
+ hasNewLayoutState,
1199
+ hasSlice,
1200
+ headerActions,
1201
+ headerSlice,
1202
+ headless,
1203
+ hideOverlay,
1204
+ i18n,
1205
+ i18nRegistry2 as i18nRegistry,
1206
+ initTenantEffects,
1207
+ isDeprecationWarningsEnabled,
1208
+ layout,
1209
+ layoutDomainReducers,
1210
+ layoutReducer,
1211
+ legacySelectors,
1212
+ menuActions,
1213
+ menuSlice,
1214
+ minimal,
1215
+ navigateTo,
1216
+ navigateToScreen,
1217
+ navigation,
1218
+ openPopup,
1219
+ overlayActions,
1220
+ overlaySlice,
1221
+ popupActions,
1222
+ popupSlice,
1223
+ presets,
1224
+ registerSlice2 as registerSlice,
1225
+ routeRegistry,
1226
+ routing,
1227
+ screenActions,
1228
+ screenSlice,
1229
+ screensetRegistry,
1230
+ screensets,
1231
+ setActiveScreen,
1232
+ setDeprecationWarnings,
1233
+ setFooterConfig,
1234
+ setFooterVisible,
1235
+ setLoading as setHeaderLoading,
1236
+ setMenuCollapsed,
1237
+ setMenuConfig,
1238
+ setMenuItems,
1239
+ setMenuVisible,
1240
+ setOverlayVisible,
1241
+ setScreenLoading,
1242
+ setSidebarCollapsed,
1243
+ setSidebarConfig,
1244
+ setSidebarContent,
1245
+ setSidebarPosition,
1246
+ setSidebarTitle,
1247
+ setSidebarVisible,
1248
+ setSidebarWidth,
1249
+ setTenant,
1250
+ setTenantLoading,
1251
+ setTenantLoadingState,
1252
+ setUser,
1253
+ showOverlay,
1254
+ sidebarActions,
1255
+ sidebarSlice,
1256
+ tenantActions,
1257
+ tenantSlice_default as tenantReducer,
1258
+ tenantSlice,
1259
+ themeRegistry,
1260
+ themes,
1261
+ toggleMenu,
1262
+ toggleSidebar
1263
+ };
1264
+ //# sourceMappingURL=index.js.map