kimu-core 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/.editorconfig +30 -0
  2. package/.gitattributes +11 -0
  3. package/.github/FUNDING.yml +8 -0
  4. package/.github/copilot-instructions.md +103 -0
  5. package/.github/kimu-copilot-instructions.md +3779 -0
  6. package/.github/workflows/deploy-demo.yml +39 -0
  7. package/AUTHORS.md +20 -0
  8. package/CHANGELOG.md +20 -0
  9. package/CODE_GUIDELINES.md +165 -0
  10. package/CODE_OF_CONDUCT.md +47 -0
  11. package/CONTRIBUTING.md +62 -0
  12. package/FUNDING.md +31 -0
  13. package/ISSUE_GUIDELINES.md +74 -0
  14. package/LICENSE +17 -0
  15. package/LICENSE.it.md +17 -0
  16. package/MPL-2.0.txt +373 -0
  17. package/NOTICE +65 -0
  18. package/README-KIMU.md +40 -0
  19. package/README.it.md +208 -0
  20. package/README.md +266 -0
  21. package/SECURITY.md +64 -0
  22. package/docs/get-started-en.md +207 -0
  23. package/docs/images/icon.svg +64 -0
  24. package/docs/images/logo_kimu.png +0 -0
  25. package/docs/index.md +29 -0
  26. package/env/dev.config.json +6 -0
  27. package/env/local.config.json +6 -0
  28. package/env/prod.config.json +6 -0
  29. package/env/staging.config.json +6 -0
  30. package/env/test.config.json +4 -0
  31. package/icon.svg +10 -0
  32. package/logo_kimu.png +0 -0
  33. package/package.json +79 -0
  34. package/public/favicon.svg +64 -0
  35. package/public/logo_kimu.svg +1 -0
  36. package/scripts/build-all-config.js +59 -0
  37. package/scripts/build-all-core.js +65 -0
  38. package/scripts/build-all-extensions.js +64 -0
  39. package/scripts/build-all-modules.js +99 -0
  40. package/scripts/build-extension.js +60 -0
  41. package/scripts/clear-kimu-build.js +31 -0
  42. package/scripts/generate-kimu-build-config.js +79 -0
  43. package/scripts/install-module.js +162 -0
  44. package/scripts/list-modules.js +109 -0
  45. package/scripts/minify-css-assets.js +82 -0
  46. package/scripts/remove-module.js +122 -0
  47. package/scripts/utils/fix-imports.js +85 -0
  48. package/src/assets/index.css +43 -0
  49. package/src/assets/kimu-style.css +84 -0
  50. package/src/assets/style.css +116 -0
  51. package/src/config/kimu-base-config.json +5 -0
  52. package/src/core/index.ts +47 -0
  53. package/src/core/kimu-app.ts +76 -0
  54. package/src/core/kimu-asset-manager.ts +167 -0
  55. package/src/core/kimu-component-element.ts +325 -0
  56. package/src/core/kimu-component.ts +33 -0
  57. package/src/core/kimu-engine.ts +188 -0
  58. package/src/core/kimu-extension-manager.ts +281 -0
  59. package/src/core/kimu-global-styles.ts +136 -0
  60. package/src/core/kimu-module-manager.ts +69 -0
  61. package/src/core/kimu-module.ts +21 -0
  62. package/src/core/kimu-path-config.ts +127 -0
  63. package/src/core/kimu-reactive.ts +196 -0
  64. package/src/core/kimu-render.ts +91 -0
  65. package/src/core/kimu-store.ts +147 -0
  66. package/src/core/kimu-types.ts +65 -0
  67. package/src/extensions/.gitkeep +0 -0
  68. package/src/extensions/extensions-manifest.json +13 -0
  69. package/src/extensions/kimu-home/component.ts +80 -0
  70. package/src/extensions/kimu-home/lang/en.json +5 -0
  71. package/src/extensions/kimu-home/lang/it.json +5 -0
  72. package/src/extensions/kimu-home/style.css +61 -0
  73. package/src/extensions/kimu-home/view.html +51 -0
  74. package/src/index.html +26 -0
  75. package/src/main.ts +68 -0
  76. package/src/modules/.gitkeep +0 -0
  77. package/src/modules/README.md +79 -0
  78. package/src/modules/i18n/README.it.md +63 -0
  79. package/src/modules/i18n/README.md +63 -0
  80. package/src/modules/i18n/kimu-global-lang.ts +26 -0
  81. package/src/modules/i18n/kimu-i18n-service.ts +108 -0
  82. package/src/modules/i18n/manifest.json +22 -0
  83. package/src/modules/i18n/module.ts +39 -0
  84. package/src/modules/modules-manifest.json +12 -0
  85. package/src/modules-repository/README.md +108 -0
  86. package/src/modules-repository/api-axios/CHANGELOG.md +48 -0
  87. package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -0
  88. package/src/modules-repository/api-axios/README.md +304 -0
  89. package/src/modules-repository/api-axios/api-axios-service.ts +355 -0
  90. package/src/modules-repository/api-axios/examples.ts +293 -0
  91. package/src/modules-repository/api-axios/index.ts +19 -0
  92. package/src/modules-repository/api-axios/interfaces.ts +71 -0
  93. package/src/modules-repository/api-axios/module.ts +41 -0
  94. package/src/modules-repository/api-core/CHANGELOG.md +42 -0
  95. package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -0
  96. package/src/modules-repository/api-core/README.md +435 -0
  97. package/src/modules-repository/api-core/api-core-service.ts +289 -0
  98. package/src/modules-repository/api-core/examples.ts +432 -0
  99. package/src/modules-repository/api-core/index.ts +8 -0
  100. package/src/modules-repository/api-core/interfaces.ts +83 -0
  101. package/src/modules-repository/api-core/module.ts +30 -0
  102. package/src/modules-repository/event-bus/README.md +273 -0
  103. package/src/modules-repository/event-bus/event-bus-service.ts +176 -0
  104. package/src/modules-repository/event-bus/module.ts +30 -0
  105. package/src/modules-repository/i18n/README.it.md +63 -0
  106. package/src/modules-repository/i18n/README.md +63 -0
  107. package/src/modules-repository/i18n/kimu-global-lang.ts +26 -0
  108. package/src/modules-repository/i18n/kimu-i18n-service.ts +108 -0
  109. package/src/modules-repository/i18n/manifest.json +22 -0
  110. package/src/modules-repository/i18n/module.ts +39 -0
  111. package/src/modules-repository/notification/README.md +423 -0
  112. package/src/modules-repository/notification/module.ts +30 -0
  113. package/src/modules-repository/notification/notification-service.ts +436 -0
  114. package/src/modules-repository/router/README.it.md +39 -0
  115. package/src/modules-repository/router/README.md +39 -0
  116. package/src/modules-repository/router/manifest.json +21 -0
  117. package/src/modules-repository/router/module.ts +23 -0
  118. package/src/modules-repository/router/router.ts +144 -0
  119. package/src/modules-repository/state/README.md +409 -0
  120. package/src/modules-repository/state/module.ts +30 -0
  121. package/src/modules-repository/state/state-service.ts +296 -0
  122. package/src/modules-repository/theme/README.md +267 -0
  123. package/src/modules-repository/theme/module.ts +30 -0
  124. package/src/modules-repository/theme/pre-build.js +40 -0
  125. package/src/modules-repository/theme/theme-service.ts +389 -0
  126. package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -0
  127. package/src/modules-repository/theme/themes/theme-cozy.css +111 -0
  128. package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -0
  129. package/src/modules-repository/theme/themes/theme-dark.css +79 -0
  130. package/src/modules-repository/theme/themes/theme-forest.css +171 -0
  131. package/src/modules-repository/theme/themes/theme-gold.css +100 -0
  132. package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -0
  133. package/src/modules-repository/theme/themes/theme-lava.css +101 -0
  134. package/src/modules-repository/theme/themes/theme-lavender.css +90 -0
  135. package/src/modules-repository/theme/themes/theme-light.css +79 -0
  136. package/src/modules-repository/theme/themes/theme-matrix.css +103 -0
  137. package/src/modules-repository/theme/themes/theme-midnight.css +81 -0
  138. package/src/modules-repository/theme/themes/theme-nord.css +94 -0
  139. package/src/modules-repository/theme/themes/theme-ocean.css +84 -0
  140. package/src/modules-repository/theme/themes/theme-retro80s.css +343 -0
  141. package/src/modules-repository/theme/themes/theme-sunset.css +62 -0
  142. package/src/modules-repository/theme/themes-config.d.ts +27 -0
  143. package/src/modules-repository/theme/themes-config.json +213 -0
  144. package/src/vite-env.d.ts +1 -0
  145. package/tsconfig.json +33 -0
  146. package/vite.config.ts +99 -0
@@ -0,0 +1,281 @@
1
+ import { KimuStore } from './kimu-store';
2
+ import { KimuExtensionMeta } from './kimu-types';
3
+ import { KimuPathConfig } from './kimu-path-config';
4
+
5
+ /**
6
+ * Get the extension manifest path with proper base path resolution
7
+ */
8
+ const getExtManifestPath = () => KimuPathConfig.resolvePath('/extensions/extensions-manifest.json');
9
+
10
+ /**
11
+ * The `KimuExtensionManager` class manages extensions in the Kimu framework.
12
+ * It handles the initialization, loading, and runtime management of extensions.
13
+ *
14
+ * Key functionalities:
15
+ * - Loads the initial manifest of extensions.
16
+ * - Synchronizes the database with the manifest.
17
+ * - Dynamically loads extensions at runtime.
18
+ * - Provides methods to list, save, disable, and remove extensions.
19
+ * - Maintains a registry of loaded extensions to avoid duplicate loading.
20
+ */
21
+ export class KimuExtensionManager {
22
+
23
+ /** Singleton instance of the extension manager */
24
+ private static _instance: KimuExtensionManager;
25
+
26
+ /** Registered extensions in the system (stored in IndexedDB) */
27
+ private _extensions: Map<string, KimuExtensionMeta> = new Map<string, KimuExtensionMeta>();
28
+
29
+ /** Map to track whether an extension has been loaded */
30
+ private _loaded: Map<string, boolean> = new Map();
31
+
32
+ /** Map to track loading promises to avoid duplicate loading */
33
+ private _loadingPromises: Map<string, Promise<void>> = new Map();
34
+
35
+ /**
36
+ * Retrieves the singleton instance of the extension manager.
37
+ * If the instance does not exist, it initializes a new one.
38
+ */
39
+ static getInstance(): KimuExtensionManager {
40
+ if (!this._instance) {
41
+ // Check if an existing instance is stored in the global window object
42
+ const existing = (window as any).__kimu_extension_manager__;
43
+ if (existing) {
44
+ this._instance = existing;
45
+ } else {
46
+ this._instance = new KimuExtensionManager();
47
+ (window as any).__kimu_extension_manager__ = this._instance;
48
+ }
49
+ }
50
+ return this._instance;
51
+ }
52
+
53
+ /**
54
+ * Initializes IndexedDB and loads the initial manifest on the first run.
55
+ */
56
+ async init(): Promise<void> {
57
+ // Initialize the extension store (IndexedDB)
58
+ await KimuStore.init();
59
+ // Check if the database is empty (first run)
60
+ // If empty, loads the initial manifest and saves it to the database
61
+ const isEmpty = await KimuStore.isEmpty();
62
+ const manifest = await this.loadInitialManifest();
63
+ if (isEmpty) {
64
+ // Load the initial manifest and save extensions to the database
65
+ console.log('[KimuExtensionManager] ⚠️ Database is empty, loading initial manifest...');
66
+ for (const entry of manifest) {
67
+ await KimuStore.save(entry);
68
+ }
69
+ console.log('[KimuExtensionManager] ✅ KimuStore (IndexedDB) initialized with the manifest JSON');
70
+ } else {
71
+ //console.log('[KimuExtensionManager] 🔄 DB present: sync with json manifest');
72
+ await this.syncWithManifest(manifest);
73
+ }
74
+ const all = await KimuStore.list();
75
+ this._extensions.clear();
76
+ this._loaded.clear();
77
+ for (const ext of all) {
78
+ this._extensions.set(ext.tag, ext);
79
+ this._loaded.set(ext.tag, false);
80
+ }
81
+ // console.log('[KimuExtensionManager] 📦 Extensions available:', this._extensions);
82
+ }
83
+
84
+ /**
85
+ * Synchronizes the database with the initial manifest of extensions.
86
+ * Adds new extensions from the manifest and updates existing ones if necessary.
87
+ * If it is present in the DB but not in the manifest, remove it.
88
+ */
89
+ private async syncWithManifest(manifest: KimuExtensionMeta[]): Promise<void> {
90
+ const current = await KimuStore.list();
91
+ const currentMap = new Map(current.map(e => [e.tag, e]));
92
+
93
+ for (const entry of manifest) {
94
+ const existing = currentMap.get(entry.tag);
95
+
96
+ if (!existing) {
97
+ console.log(`[KimuExtensionManager] ➕ Adding new extension: ${entry.tag}`);
98
+ await KimuStore.save(entry);
99
+ } else {
100
+ // Puoi raffinare il confronto: qui controlliamo solo se version o path sono cambiati
101
+ const changed =
102
+ existing.version !== entry.version ||
103
+ existing.path !== entry.path ||
104
+ existing.name !== entry.name;
105
+
106
+ if (changed) {
107
+ console.log(`[KimuExtensionManager] 🔁 Updating extension: ${entry.tag}`);
108
+ await KimuStore.save(entry);
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Loads the initial manifest of extensions (only once).
116
+ */
117
+ private async loadInitialManifest(): Promise<KimuExtensionMeta[]> {
118
+ // console.log('[KimuExtensionManager] Loading initial manifest...');
119
+ // Load the extension manifest
120
+ try {
121
+ const manifestPath = getExtManifestPath();
122
+ const res = await fetch(manifestPath);
123
+ if (!res.ok) {
124
+ throw new Error('File not found');
125
+ }
126
+ const manifest: KimuExtensionMeta[] = await res.json();
127
+ return manifest;
128
+ } catch (err) {
129
+ console.warn('[KimuExtensionManager] ⚠️ Initial manifest not loaded:', err);
130
+ return [];
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Reloads the extension manifest from the remote source.
136
+ */
137
+ async reloadManifest(): Promise<void> {
138
+ // console.log('[KimuExtensionManager] Reload manifest...');
139
+ const newManifest = await this.loadInitialManifest();
140
+ for (const entry of newManifest) {
141
+ await this.save(entry);
142
+ }
143
+ console.log('[KimuExtensionManager] 🔁 Manifest manually reloaded');
144
+ }
145
+
146
+ /**
147
+ * Returns all extensions currently registered in memory.
148
+ */
149
+ listAvailable(): KimuExtensionMeta[] {
150
+ return Array.from(this._extensions.values());
151
+ }
152
+
153
+ /**
154
+ * Returns all extensions saved in IndexedDB.
155
+ */
156
+ async list(): Promise<KimuExtensionMeta[]> {
157
+ return await KimuStore.list();
158
+ }
159
+
160
+ /**
161
+ * Retrieves an extension from IndexedDB by its tag.
162
+ */
163
+ get(tag: string): KimuExtensionMeta | undefined {
164
+ return this._extensions.get(tag);
165
+ }
166
+
167
+ /**
168
+ * Returns the tags of extensions already loaded dynamically.
169
+ */
170
+ getTags(): string[] {
171
+ return Array.from(this._extensions.keys());
172
+ }
173
+
174
+ /**
175
+ * Checks if an extension has been loaded into the system.
176
+ */
177
+ isLoaded(tag: string): boolean {
178
+ return this._loaded.get(tag) ?? false;
179
+ }
180
+
181
+ /**
182
+ * Dynamically loads an extension at runtime (only once).
183
+ */
184
+ async load(tag: string): Promise<void> {
185
+ //console.log(`[KimuExtensionManager] Load extension: ${tag}`);
186
+
187
+ // Check if the extension is already loaded
188
+ if (!this._extensions.has(tag)) {
189
+ console.log(`[KimuExtensionManager] ⚠️ Extension not present: ${tag}`);
190
+ return;
191
+ }
192
+
193
+ // Check if already loaded
194
+ if (this.isLoaded(tag)) {
195
+ //console.log(`[KimuExtensionManager] ⚠️ Extension already loaded: ${tag}`);
196
+ return;
197
+ }
198
+
199
+ // Check if already loading
200
+ if (this._loadingPromises.has(tag)) {
201
+ return this._loadingPromises.get(tag);
202
+ }
203
+
204
+ const ext = this._extensions.get(tag);
205
+ if (!ext) {
206
+ console.warn(`[KimuExtensionManager] ⚠️ Extension not found: ${tag}`);
207
+ return;
208
+ }
209
+
210
+ if (!ext.path) {
211
+ console.warn(`[KimuExtensionManager] ⚠️ Path not defined for extension: ${tag}`);
212
+ return;
213
+ }
214
+
215
+ // Create loading promise
216
+ const loadingPromise = this._loadExtension(tag, ext);
217
+ this._loadingPromises.set(tag, loadingPromise);
218
+
219
+ try {
220
+ await loadingPromise;
221
+ } finally {
222
+ this._loadingPromises.delete(tag);
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Internal method to load an extension
228
+ */
229
+ private async _loadExtension(tag: string, ext: KimuExtensionMeta): Promise<void> {
230
+ try {
231
+ const componentPath = KimuPathConfig.resolvePath(`/extensions/${ext.path}/component.js`);
232
+ await import(/* @vite-ignore */ componentPath);
233
+ this._loaded.set(tag, true);
234
+ // console.log(`[KimuExtensionManager] ✅ Extension loaded: ${tag}`);
235
+ } catch (err) {
236
+ console.error(`[KimuExtensionManager] ❌ Error loading Extension tag "${tag}":`, err);
237
+ throw err;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Saves an extension to the database.
243
+ */
244
+ async save(entry: KimuExtensionMeta): Promise<void> {
245
+ await KimuStore.save(entry);
246
+ this._extensions.set(entry.tag, entry);
247
+ this._loaded.set(entry.tag, false);
248
+ }
249
+
250
+ /**
251
+ * Disables an extension.
252
+ */
253
+ async disable(tag: string): Promise<void> {
254
+ const ext = await this.get(tag);
255
+ if (ext) {
256
+ ext.enabled = false;
257
+ await this.save(ext);
258
+ console.log(`[KimuExtensionManager] ⛔ Extension disabled: ${tag}`);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Removes an extension from the database.
264
+ */
265
+ async remove(tag: string): Promise<void> {
266
+ await KimuStore.remove(tag);
267
+ this._extensions.delete(tag);
268
+ this._loaded.delete(tag);
269
+ console.log(`[KimuExtensionManager] 🗑️ Extension removed: ${tag}`);
270
+ }
271
+
272
+ /**
273
+ * Resets the database completely. Clears all data (use with caution).
274
+ */
275
+ async reset(): Promise<void> {
276
+ await KimuStore.clear();
277
+ this._extensions.clear();
278
+ this._loaded.clear();
279
+ console.log('[KimuExtensionManager] 🔄 Complete database reset');
280
+ }
281
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * KimuGlobalStyles manages global CSS files that should be injected into all extensions.
3
+ * This allows modules (like the theme module) to register additional global styles
4
+ * without hardcoding dependencies in the core framework.
5
+ *
6
+ * Usage:
7
+ * - Core always injects 'assets/kimu-style.css' as the base style
8
+ * - Modules can register additional global CSS using registerGlobalStyle()
9
+ * - All registered styles are automatically injected into every extension's shadow root
10
+ */
11
+ export class KimuGlobalStyles {
12
+ /** List of global CSS files to inject in all extensions */
13
+ private static _globalStyles: Array<{ id: string; path: string }> = [
14
+ // Default KIMU base styles - always included
15
+ { id: 'kimu-style-default', path: 'assets/kimu-style.css' }
16
+ ];
17
+
18
+ /**
19
+ * Register a global CSS file to be injected in all extensions.
20
+ * This is useful for modules that want to provide global theming or styling.
21
+ *
22
+ * @param id - Unique identifier for the style (e.g., 'theme-dark', 'theme-light')
23
+ * @param path - Path to the CSS file relative to the base path
24
+ *
25
+ * @example
26
+ * // In a theme module
27
+ * KimuGlobalStyles.registerGlobalStyle('theme-active', 'themes/theme-dark.css');
28
+ */
29
+ static registerGlobalStyle(id: string, path: string): void {
30
+ // Check if already registered
31
+ const existing = this._globalStyles.find(s => s.id === id);
32
+ if (existing) {
33
+ // Update path if already registered
34
+ existing.path = path;
35
+ console.log(`[KimuGlobalStyles] Updated global style: ${id} -> ${path}`);
36
+ } else {
37
+ // Add new global style
38
+ this._globalStyles.push({ id, path });
39
+ console.log(`[KimuGlobalStyles] Registered global style: ${id} -> ${path}`);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Unregister a global CSS file.
45
+ *
46
+ * @param id - Unique identifier of the style to remove
47
+ */
48
+ static unregisterGlobalStyle(id: string): void {
49
+ const index = this._globalStyles.findIndex(s => s.id === id);
50
+ if (index !== -1) {
51
+ this._globalStyles.splice(index, 1);
52
+ console.log(`[KimuGlobalStyles] Unregistered global style: ${id}`);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Get all registered global styles.
58
+ * Used internally by KimuComponentElement to inject styles into extensions.
59
+ *
60
+ * @returns Array of registered global styles
61
+ */
62
+ static getGlobalStyles(): Array<{ id: string; path: string }> {
63
+ return [...this._globalStyles]; // Return a copy to prevent external modifications
64
+ }
65
+
66
+ /**
67
+ * Clear all registered styles (useful for testing or reset scenarios).
68
+ * Note: This will also remove the default kimu-style.css.
69
+ */
70
+ static clearAllStyles(): void {
71
+ this._globalStyles = [
72
+ { id: 'kimu-style-default', path: 'assets/kimu-style.css' }
73
+ ];
74
+ console.log('[KimuGlobalStyles] Cleared all global styles, keeping only default');
75
+ }
76
+
77
+ /**
78
+ * Get the number of registered global styles.
79
+ */
80
+ static getStyleCount(): number {
81
+ return this._globalStyles.length;
82
+ }
83
+
84
+ /**
85
+ * Check if a specific style ID is registered.
86
+ */
87
+ static hasStyle(id: string): boolean {
88
+ return this._globalStyles.some(s => s.id === id);
89
+ }
90
+
91
+ /**
92
+ * Update a specific global style in all currently loaded extensions.
93
+ * This is useful when a theme changes and needs to be propagated to all extensions.
94
+ *
95
+ * @param id - Style ID to update (e.g., 'kimu-theme-active')
96
+ *
97
+ * Note: This method uses KimuEngine.injectStyle() to update styles in all shadow roots.
98
+ * It requires KimuEngine to be imported dynamically to avoid circular dependencies.
99
+ */
100
+ static async updateStyleInAllExtensions(id: string): Promise<void> {
101
+ const style = this._globalStyles.find(s => s.id === id);
102
+ if (!style) {
103
+ console.warn(`[KimuGlobalStyles] Style "${id}" not found, cannot update`);
104
+ return;
105
+ }
106
+
107
+ // Find all custom elements (extensions) in the DOM
108
+ const allCustomElements = document.querySelectorAll('*');
109
+ const extensions: HTMLElement[] = [];
110
+
111
+ allCustomElements.forEach((el) => {
112
+ // Check if element is a custom element with shadow root
113
+ if (el.tagName.includes('-') && (el as any).shadowRoot) {
114
+ extensions.push(el as HTMLElement);
115
+ }
116
+ });
117
+
118
+ if (extensions.length === 0) {
119
+ console.log('[KimuGlobalStyles] No extensions found to update');
120
+ return;
121
+ }
122
+
123
+ // Dynamically import KimuEngine to avoid circular dependency
124
+ const { KimuEngine } = await import('./kimu-engine');
125
+
126
+ // Update style in all extensions
127
+ console.log(`[KimuGlobalStyles] Updating style "${id}" in ${extensions.length} extensions`);
128
+ for (const ext of extensions) {
129
+ try {
130
+ await KimuEngine.injectStyle(ext, style.path, style.id);
131
+ } catch (error) {
132
+ console.warn(`[KimuGlobalStyles] Failed to update style in ${ext.tagName}:`, error);
133
+ }
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,69 @@
1
+ import { KimuModule } from './kimu-module';
2
+ import { KimuModuleOptions } from './kimu-types';
3
+ import { KimuPathConfig } from './kimu-path-config';
4
+
5
+ export class KimuModuleManager {
6
+ private static instance: KimuModuleManager | null = null;
7
+ private modules: Map<string, KimuModule> = new Map();
8
+
9
+ static getInstance(): KimuModuleManager {
10
+ if (!KimuModuleManager.instance) {
11
+ KimuModuleManager.instance = new KimuModuleManager();
12
+ }
13
+ return KimuModuleManager.instance;
14
+ }
15
+
16
+ async loadModule(name: string, options?: KimuModuleOptions): Promise<KimuModule> {
17
+ // Check if module already exists (singleton support)
18
+ if (this.modules.has(name)) {
19
+ const existing = this.modules.get(name)!;
20
+ if (existing.singleton) {
21
+ return existing;
22
+ }
23
+ }
24
+
25
+ // Import dinamico del modulo con base path
26
+ const modPath = KimuPathConfig.resolvePath(`/modules/${name}/module.js`);
27
+ const modImport = await import(/* @vite-ignore */ modPath);
28
+ const instance: KimuModule = new modImport.default(name, '1.0.0', options);
29
+
30
+ if (instance.onInit) await instance.onInit();
31
+ this.modules.set(name, instance);
32
+ return instance;
33
+ }
34
+
35
+ getModule(name: string): KimuModule | undefined {
36
+ return this.modules.get(name);
37
+ }
38
+
39
+ async closeModule(name: string): Promise<void> {
40
+ const mod = this.modules.get(name);
41
+ if (mod && mod.onClose) await mod.onClose();
42
+ this.modules.delete(name);
43
+ }
44
+
45
+ getService(name: string): any {
46
+ const mod = this.modules.get(name);
47
+ return mod ? mod.getService() : undefined;
48
+ }
49
+
50
+ // Convenience methods for commonly used modules
51
+ async getI18nService(options?: KimuModuleOptions): Promise<any> {
52
+ const module = await this.loadModule('i18n', options);
53
+ return module.getService();
54
+ }
55
+
56
+ async getRouterService(options?: KimuModuleOptions): Promise<any> {
57
+ const module = await this.loadModule('router', options);
58
+ return module.getService();
59
+ }
60
+
61
+ // Static convenience methods for global access
62
+ static async getI18nService(options?: KimuModuleOptions): Promise<any> {
63
+ return KimuModuleManager.getInstance().getI18nService(options);
64
+ }
65
+
66
+ static async getRouterService(options?: KimuModuleOptions): Promise<any> {
67
+ return KimuModuleManager.getInstance().getRouterService(options);
68
+ }
69
+ }
@@ -0,0 +1,21 @@
1
+ import { KimuModuleOptions } from './kimu-types';
2
+
3
+ export abstract class KimuModule {
4
+ public name: string;
5
+ public version: string;
6
+ public singleton?: boolean = false;
7
+ public options?: KimuModuleOptions;
8
+
9
+ constructor(name: string, version: string, options?: KimuModuleOptions) {
10
+ this.name = name;
11
+ this.version = version;
12
+ this.options = options;
13
+ }
14
+
15
+ // Lifecycle hooks
16
+ onInit?(): void | Promise<void>;
17
+ onClose?(): void | Promise<void>;
18
+
19
+ // API per servizi/esportazioni
20
+ abstract getService(): any;
21
+ }
@@ -0,0 +1,127 @@
1
+ import { KimuBuildConfig } from '../config/kimu-build-config';
2
+
3
+ /**
4
+ * KimuPathConfig handles base path configuration for deployments in subdirectories.
5
+ * It integrates with the existing build configuration system and supports:
6
+ * - Automatic initialization from generated build config
7
+ * - Runtime path resolution for all framework resources
8
+ * - Support for deployment in any server subdirectory
9
+ *
10
+ * Usage:
11
+ * - Paths are configured in env/*.config.json files
12
+ * - Can be overridden with environment variables (KIMU_BASE_PATH)
13
+ * - Automatically resolves all framework resource paths
14
+ */
15
+ export class KimuPathConfig {
16
+ /** Current base path (without trailing slash, empty for root) */
17
+ private static _basePath: string | null = null;
18
+
19
+ /** Initialization flag to avoid multiple initializations */
20
+ private static _initialized: boolean = false;
21
+
22
+ /**
23
+ * Initialize from generated build config.
24
+ * Called automatically on first path resolution.
25
+ */
26
+ static initialize(): void {
27
+ if (this._initialized) return;
28
+
29
+ try {
30
+ // Get base path from generated configuration
31
+ const configBasePath = (KimuBuildConfig.build as any)['base-path'] || '/';
32
+ this.setBasePath(configBasePath);
33
+ this._initialized = true;
34
+
35
+ console.log(`[KimuPathConfig] Initialized with base path: "${this._basePath || '/'}"`);
36
+ } catch (error) {
37
+ console.warn('[KimuPathConfig] Failed to initialize from build config, using root path', error);
38
+ this.setBasePath('/');
39
+ this._initialized = true;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Set base path manually (mainly for testing or runtime override).
45
+ * @param path The base path to set (e.g., "/", "/dist/", "/my-app/")
46
+ */
47
+ static setBasePath(path: string): void {
48
+ // Normalize path: ensure starts with / and doesn't end with / (except root)
49
+ if (!path.startsWith('/')) path = '/' + path;
50
+ if (path.length > 1 && path.endsWith('/')) path = path.slice(0, -1);
51
+
52
+ // Store as empty string for root path, or the normalized path
53
+ this._basePath = path === '/' ? '' : path;
54
+ }
55
+
56
+ /**
57
+ * Get current base path.
58
+ * @returns The current base path (empty string for root)
59
+ */
60
+ static getBasePath(): string {
61
+ if (!this._initialized) this.initialize();
62
+ return this._basePath || '';
63
+ }
64
+
65
+ /**
66
+ * Resolve an absolute path with the configured base path.
67
+ * @param path The path to resolve (e.g., "/extensions/manifest.json")
68
+ * @returns The resolved path with base path prepended
69
+ */
70
+ static resolvePath(path: string): string {
71
+ if (!this._initialized) this.initialize();
72
+
73
+ // Skip external URLs
74
+ if (this.isExternalUrl(path)) return path;
75
+
76
+ // Skip relative paths (they're already relative to current location)
77
+ if (!path.startsWith('/')) return path;
78
+
79
+ // Apply base path to absolute paths
80
+ const basePath = this._basePath || '';
81
+ return basePath + path;
82
+ }
83
+
84
+ /**
85
+ * Check if a path is an external URL.
86
+ * @param path The path to check
87
+ * @returns True if the path is an external URL
88
+ */
89
+ private static isExternalUrl(path: string): boolean {
90
+ return path.startsWith('http://') ||
91
+ path.startsWith('https://') ||
92
+ path.startsWith('//') ||
93
+ path.startsWith('mailto:') ||
94
+ path.startsWith('tel:');
95
+ }
96
+
97
+ /**
98
+ * Force re-initialization (mainly for testing).
99
+ */
100
+ static reset(): void {
101
+ this._initialized = false;
102
+ this._basePath = null;
103
+ }
104
+
105
+ /**
106
+ * Get full URL for a path (combines base URL from config with resolved path).
107
+ * @param path The path to convert to full URL
108
+ * @returns Full URL with base URL and path
109
+ */
110
+ static getFullUrl(path: string): string {
111
+ if (!this._initialized) this.initialize();
112
+
113
+ try {
114
+ const webUrl = (KimuBuildConfig.build as any)['web-url'] || window.location.origin;
115
+ const resolvedPath = this.resolvePath(path);
116
+
117
+ // If path is already a full URL, return as-is
118
+ if (this.isExternalUrl(resolvedPath)) return resolvedPath;
119
+
120
+ // Combine base URL with resolved path
121
+ return webUrl + resolvedPath;
122
+ } catch (error) {
123
+ console.warn('[KimuPathConfig] Failed to get full URL, returning resolved path', error);
124
+ return this.resolvePath(path);
125
+ }
126
+ }
127
+ }