@agiflowai/style-system 0.0.3 → 0.0.5

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/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_stdio = require('./stdio-BlNvX94v.cjs');
2
+ const require_stdio = require('./stdio-D3tBpCoD.cjs');
3
3
  let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
4
4
  __agiflowai_aicode_utils = require_stdio.__toESM(__agiflowai_aicode_utils);
5
5
  let commander = require("commander");
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as ListAppComponentsTool, i as ListThemesTool, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, r as ListSharedComponentsTool, t as StdioTransportHandler, u as getBundlerServiceFromConfig } from "./stdio-CGaoEmM8.js";
2
+ import { a as ListAppComponentsTool, i as ListThemesTool, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, r as ListSharedComponentsTool, t as StdioTransportHandler, u as getBundlerServiceFromConfig } from "./stdio-DvPtx0WS.js";
3
3
  import { print } from "@agiflowai/aicode-utils";
4
4
  import { Command } from "commander";
5
5
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_stdio = require('./stdio-BlNvX94v.cjs');
1
+ const require_stdio = require('./stdio-D3tBpCoD.cjs');
2
2
 
3
3
  exports.BaseBundlerService = require_stdio.BaseBundlerService;
4
4
  exports.BaseCSSClassesService = require_stdio.BaseCSSClassesService;
package/dist/index.d.cts CHANGED
@@ -641,19 +641,25 @@ declare class ComponentRendererService {
641
641
  */
642
642
  renderComponent(componentInfo: ComponentInfo, options?: RenderOptions): Promise<RenderResult>;
643
643
  /**
644
- * Maximum number of screenshot files to keep in temp directory.
644
+ * Default maximum number of screenshot files to keep in temp directory.
645
645
  * Prevents disk space issues on long-running servers.
646
646
  */
647
- private static readonly MAX_TEMP_FILES;
647
+ private static readonly DEFAULT_MAX_TEMP_FILES;
648
+ /**
649
+ * Number of recent files to keep when cleaning up on dispose.
650
+ */
651
+ private static readonly DISPOSE_KEEP_COUNT;
648
652
  /**
649
653
  * Clean up old rendered files.
650
654
  * Removes files older than the specified duration and enforces a max file count.
651
655
  * @param olderThanMs - Remove files older than this duration (default: 1 hour)
656
+ * @param keepCount - Maximum number of recent files to keep (default: 100)
652
657
  */
653
- cleanup(olderThanMs?: number): Promise<void>;
658
+ cleanup(olderThanMs?: number, keepCount?: number): Promise<void>;
654
659
  /**
655
- * Cleanup bundler server and temp files.
660
+ * Cleanup bundler server resources and old temp files.
656
661
  * Called on service shutdown.
662
+ * Keeps the most recent files for caching purposes.
657
663
  */
658
664
  dispose(): Promise<void>;
659
665
  }
package/dist/index.d.ts CHANGED
@@ -641,19 +641,25 @@ declare class ComponentRendererService {
641
641
  */
642
642
  renderComponent(componentInfo: ComponentInfo, options?: RenderOptions): Promise<RenderResult>;
643
643
  /**
644
- * Maximum number of screenshot files to keep in temp directory.
644
+ * Default maximum number of screenshot files to keep in temp directory.
645
645
  * Prevents disk space issues on long-running servers.
646
646
  */
647
- private static readonly MAX_TEMP_FILES;
647
+ private static readonly DEFAULT_MAX_TEMP_FILES;
648
+ /**
649
+ * Number of recent files to keep when cleaning up on dispose.
650
+ */
651
+ private static readonly DISPOSE_KEEP_COUNT;
648
652
  /**
649
653
  * Clean up old rendered files.
650
654
  * Removes files older than the specified duration and enforces a max file count.
651
655
  * @param olderThanMs - Remove files older than this duration (default: 1 hour)
656
+ * @param keepCount - Maximum number of recent files to keep (default: 100)
652
657
  */
653
- cleanup(olderThanMs?: number): Promise<void>;
658
+ cleanup(olderThanMs?: number, keepCount?: number): Promise<void>;
654
659
  /**
655
- * Cleanup bundler server and temp files.
660
+ * Cleanup bundler server resources and old temp files.
656
661
  * Called on service shutdown.
662
+ * Keeps the most recent files for caching purposes.
657
663
  */
658
664
  dispose(): Promise<void>;
659
665
  }
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as TailwindCSSClassesService, a as ListAppComponentsTool, c as ThemeService, d as ViteReactBundlerService, f as BaseBundlerService, g as DEFAULT_STYLE_SYSTEM_CONFIG, h as CSSClassesServiceFactory, i as ListThemesTool, l as createDefaultBundlerService, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, p as StoriesIndexService, r as ListSharedComponentsTool, s as ComponentRendererService, t as StdioTransportHandler, u as getBundlerServiceFromConfig, v as BaseCSSClassesService } from "./stdio-CGaoEmM8.js";
1
+ import { _ as TailwindCSSClassesService, a as ListAppComponentsTool, c as ThemeService, d as ViteReactBundlerService, f as BaseBundlerService, g as DEFAULT_STYLE_SYSTEM_CONFIG, h as CSSClassesServiceFactory, i as ListThemesTool, l as createDefaultBundlerService, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, p as StoriesIndexService, r as ListSharedComponentsTool, s as ComponentRendererService, t as StdioTransportHandler, u as getBundlerServiceFromConfig, v as BaseCSSClassesService } from "./stdio-DvPtx0WS.js";
2
2
 
3
3
  export { BaseBundlerService, BaseCSSClassesService, CSSClassesServiceFactory, ComponentRendererService, DEFAULT_STYLE_SYSTEM_CONFIG, GetCSSClassesTool, GetComponentVisualTool, ListAppComponentsTool, ListSharedComponentsTool, ListThemesTool, StdioTransportHandler, StoriesIndexService, TailwindCSSClassesService, ThemeService, ViteReactBundlerService, createDefaultBundlerService, createServer, getBundlerServiceFromConfig };
@@ -733,11 +733,11 @@ var StoriesIndexService = class {
733
733
  __agiflowai_aicode_utils.log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
734
734
  const failures = [];
735
735
  let successCount = 0;
736
- for (const filePath of storyFiles) try {
737
- await this.indexStoryFile(filePath);
738
- successCount++;
739
- } catch (error) {
740
- const errorMessage = error instanceof Error ? error.message : String(error);
736
+ const results = await Promise.allSettled(storyFiles.map((filePath) => this.indexStoryFile(filePath)));
737
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") successCount++;
738
+ else {
739
+ const filePath = storyFiles[index];
740
+ const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
741
741
  __agiflowai_aicode_utils.log.error(`[StoriesIndexService] Error indexing ${filePath}: ${errorMessage}`);
742
742
  failures.push({
743
743
  filePath,
@@ -943,12 +943,14 @@ var AppComponentsService = class {
943
943
  } catch {
944
944
  throw new Error(`App path does not exist: ${resolvedAppPath}`);
945
945
  }
946
- const appName = await this.getAppName(resolvedAppPath);
947
- const workspaceDependencies = await this.getWorkspaceDependencies(resolvedAppPath);
948
- __agiflowai_aicode_utils.log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
949
- const packageMap = await this.buildPackageMap(monorepoRoot);
950
946
  const storiesIndex = new StoriesIndexService();
951
- await storiesIndex.initialize();
947
+ const [appName, workspaceDependencies, packageMap] = await Promise.all([
948
+ this.getAppName(resolvedAppPath),
949
+ this.getWorkspaceDependencies(resolvedAppPath),
950
+ this.buildPackageMap(monorepoRoot),
951
+ storiesIndex.initialize()
952
+ ]);
953
+ __agiflowai_aicode_utils.log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
952
954
  const allComponents = storiesIndex.getAllComponents();
953
955
  const { appComponentsArray, packageComponents, totalPackageComponents } = this.categorizeComponents(allComponents, resolvedAppPath, workspaceDependencies, packageMap);
954
956
  const totalComponents = appComponentsArray.length + totalPackageComponents;
@@ -1026,16 +1028,17 @@ var AppComponentsService = class {
1026
1028
  } catch (error) {
1027
1029
  throw new Error(`Failed to scan for package.json files in ${monorepoRoot}: ${error instanceof Error ? error.message : String(error)}`);
1028
1030
  }
1029
- for (const pkgJsonPath of packageJsonFiles) try {
1031
+ const results = await Promise.allSettled(packageJsonFiles.map(async (pkgJsonPath) => {
1030
1032
  const content = await node_fs.promises.readFile(pkgJsonPath, "utf-8");
1031
- const pkgJson = JSON.parse(content);
1032
- if (pkgJson.name) {
1033
- const pkgDir = node_path.default.dirname(pkgJsonPath);
1034
- packageMap.set(pkgJson.name, pkgDir);
1035
- }
1036
- } catch (error) {
1037
- __agiflowai_aicode_utils.log.debug(`[AppComponentsService] Skipping invalid package.json at ${pkgJsonPath}:`, error);
1038
- }
1033
+ return {
1034
+ pkgJsonPath,
1035
+ name: JSON.parse(content).name
1036
+ };
1037
+ }));
1038
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled" && result.value.name) {
1039
+ const pkgDir = node_path.default.dirname(result.value.pkgJsonPath);
1040
+ packageMap.set(result.value.name, pkgDir);
1041
+ } else if (result.status === "rejected") __agiflowai_aicode_utils.log.debug(`[AppComponentsService] Skipping invalid package.json at ${packageJsonFiles[index]}:`, result.reason);
1039
1042
  return packageMap;
1040
1043
  }
1041
1044
  /**
@@ -2040,13 +2043,12 @@ var CSSThemeService = class extends BaseThemeService {
2040
2043
  const cssFilePaths = this.resolveCSSFilePaths();
2041
2044
  if (cssFilePaths.length === 0) throw new Error("No theme CSS files configured. Set themePath or cssFiles in project.json style-system config.");
2042
2045
  const themes = [];
2043
- for (const cssFilePath of cssFilePaths) try {
2046
+ const results = await Promise.allSettled(cssFilePaths.map(async (cssFilePath) => {
2044
2047
  await this.validatePath(cssFilePath);
2045
- const fileThemes = await this.extractThemesFromCSS(cssFilePath);
2046
- themes.push(...fileThemes);
2047
- } catch (error) {
2048
- __agiflowai_aicode_utils.log.warn(`[CSSThemeService] Could not process ${cssFilePath}:`, error);
2049
- }
2048
+ return this.extractThemesFromCSS(cssFilePath);
2049
+ }));
2050
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") themes.push(...result.value);
2051
+ else __agiflowai_aicode_utils.log.warn(`[CSSThemeService] Could not process ${cssFilePaths[index]}:`, result.reason);
2050
2052
  const uniqueThemes = this.deduplicateThemes(themes);
2051
2053
  return {
2052
2054
  themes: uniqueThemes.sort((a, b) => a.name.localeCompare(b.name)),
@@ -2209,13 +2211,14 @@ export default WrappedComponent;
2209
2211
  */
2210
2212
  async getInlineStyles(darkMode = false) {
2211
2213
  const cssFiles = await this.getThemeCSS();
2212
- let styles = "";
2213
- for (const cssFile of cssFiles) try {
2214
- const content = await node_fs.promises.readFile(cssFile, "utf-8");
2215
- styles += content + "\n";
2216
- } catch (error) {
2217
- __agiflowai_aicode_utils.log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
2218
- }
2214
+ let styles = (await Promise.all(cssFiles.map(async (cssFile) => {
2215
+ try {
2216
+ return await node_fs.promises.readFile(cssFile, "utf-8");
2217
+ } catch (error) {
2218
+ __agiflowai_aicode_utils.log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
2219
+ return "";
2220
+ }
2221
+ }))).filter(Boolean).join("\n");
2219
2222
  if (darkMode && styles) styles = `.dark { ${styles} }`;
2220
2223
  return styles;
2221
2224
  }
@@ -2261,19 +2264,21 @@ export default WrappedComponent;
2261
2264
  const themeFiles = (await node_fs.promises.readdir(configsPath)).filter((file) => file.endsWith(".json"));
2262
2265
  const themes = [];
2263
2266
  let activeBrand;
2264
- for (const file of themeFiles) {
2267
+ const results = await Promise.allSettled(themeFiles.map(async (file) => {
2265
2268
  const filePath = node_path.default.join(configsPath, file);
2266
2269
  const content = await node_fs.promises.readFile(filePath, "utf-8");
2267
2270
  const themeData = JSON.parse(content);
2268
- const themeName = file.replace(".json", "");
2269
- themes.push({
2270
- name: themeName,
2271
+ return {
2272
+ name: file.replace(".json", ""),
2271
2273
  fileName: file,
2272
2274
  path: filePath,
2273
2275
  colors: themeData.colors || themeData
2274
- });
2275
- if (themeName === "lightTheme" || themeName === "agimonTheme") activeBrand = themeName;
2276
- }
2276
+ };
2277
+ }));
2278
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") {
2279
+ themes.push(result.value);
2280
+ if (result.value.name === "lightTheme" || result.value.name === "agimonTheme") activeBrand = result.value.name;
2281
+ } else __agiflowai_aicode_utils.log.warn(`[ThemeService] Failed to process theme file ${themeFiles[index]}:`, result.reason);
2277
2282
  return {
2278
2283
  themes: themes.sort((a, b) => a.name.localeCompare(b.name)),
2279
2284
  activeBrand
@@ -2529,44 +2534,44 @@ var ComponentRendererService = class ComponentRendererService {
2529
2534
  }
2530
2535
  }
2531
2536
  /**
2532
- * Maximum number of screenshot files to keep in temp directory.
2537
+ * Default maximum number of screenshot files to keep in temp directory.
2533
2538
  * Prevents disk space issues on long-running servers.
2534
2539
  */
2535
- static MAX_TEMP_FILES = 100;
2540
+ static DEFAULT_MAX_TEMP_FILES = 100;
2541
+ /**
2542
+ * Number of recent files to keep when cleaning up on dispose.
2543
+ */
2544
+ static DISPOSE_KEEP_COUNT = 20;
2536
2545
  /**
2537
2546
  * Clean up old rendered files.
2538
2547
  * Removes files older than the specified duration and enforces a max file count.
2539
2548
  * @param olderThanMs - Remove files older than this duration (default: 1 hour)
2549
+ * @param keepCount - Maximum number of recent files to keep (default: 100)
2540
2550
  */
2541
- async cleanup(olderThanMs = 36e5) {
2551
+ async cleanup(olderThanMs = 36e5, keepCount = ComponentRendererService.DEFAULT_MAX_TEMP_FILES) {
2542
2552
  try {
2543
2553
  const files = await node_fs.promises.readdir(this.tmpDir);
2544
2554
  const now = Date.now();
2545
2555
  const componentFiles = [];
2546
- for (const file of files) if (file.startsWith("component-")) {
2556
+ const componentFileNames = files.filter((f) => f.startsWith("component-"));
2557
+ const statResults = await Promise.allSettled(componentFileNames.map(async (file) => {
2547
2558
  const filePath = node_path.default.join(this.tmpDir, file);
2548
- try {
2549
- const stats = await node_fs.promises.stat(filePath);
2550
- componentFiles.push({
2551
- name: file,
2552
- path: filePath,
2553
- mtime: stats.mtimeMs
2554
- });
2555
- } catch {}
2556
- }
2557
- let deletedCount = 0;
2558
- for (const file of componentFiles) if (now - file.mtime > olderThanMs) {
2559
- await node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
2560
- deletedCount++;
2561
- }
2559
+ return {
2560
+ name: file,
2561
+ path: filePath,
2562
+ mtime: (await node_fs.promises.stat(filePath)).mtimeMs
2563
+ };
2564
+ }));
2565
+ for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
2566
+ const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
2567
+ await Promise.all(oldFiles.map((file) => node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
2568
+ let deletedCount = oldFiles.length;
2562
2569
  const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
2563
- if (remainingFiles.length > ComponentRendererService.MAX_TEMP_FILES) {
2570
+ if (remainingFiles.length > keepCount) {
2564
2571
  remainingFiles.sort((a, b) => a.mtime - b.mtime);
2565
- const toDelete = remainingFiles.slice(0, remainingFiles.length - ComponentRendererService.MAX_TEMP_FILES);
2566
- for (const file of toDelete) {
2567
- await node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
2568
- deletedCount++;
2569
- }
2572
+ const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
2573
+ await Promise.all(toDelete.map((file) => node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
2574
+ deletedCount += toDelete.length;
2570
2575
  }
2571
2576
  if (deletedCount > 0) __agiflowai_aicode_utils.log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
2572
2577
  } catch (error) {
@@ -2574,12 +2579,13 @@ var ComponentRendererService = class ComponentRendererService {
2574
2579
  }
2575
2580
  }
2576
2581
  /**
2577
- * Cleanup bundler server and temp files.
2582
+ * Cleanup bundler server resources and old temp files.
2578
2583
  * Called on service shutdown.
2584
+ * Keeps the most recent files for caching purposes.
2579
2585
  */
2580
2586
  async dispose() {
2581
2587
  try {
2582
- await this.cleanup(0);
2588
+ await this.cleanup(0, ComponentRendererService.DISPOSE_KEEP_COUNT);
2583
2589
  const bundlerService = this.getBundlerService();
2584
2590
  if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
2585
2591
  } catch (error) {
@@ -2656,16 +2662,16 @@ var GetUiComponentService = class {
2656
2662
  const { componentName, appPath, storyName = this.config.defaultStoryName, darkMode = this.config.defaultDarkMode } = input;
2657
2663
  __agiflowai_aicode_utils.log.info(`[GetUiComponentService] Starting for component: ${componentName}, appPath: ${appPath}, storyName: ${storyName}`);
2658
2664
  const storiesIndex = this.storiesIndexFactory();
2665
+ let designSystemConfig;
2659
2666
  try {
2660
- await storiesIndex.initialize();
2667
+ [, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
2661
2668
  } catch (error) {
2662
- throw new Error(`Failed to initialize stories index: ${error instanceof Error ? error.message : String(error)}`);
2669
+ throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
2663
2670
  }
2664
2671
  const componentInfo = storiesIndex.findComponentByName(componentName);
2665
2672
  if (!componentInfo) throw new Error(`Component "${componentName}" not found in stories index. Ensure the component has a .stories.tsx file and has been indexed.`);
2666
2673
  __agiflowai_aicode_utils.log.info(`[GetUiComponentService] Found component: ${componentInfo.title}`);
2667
2674
  const validStoryName = this.resolveStoryName(storyName, componentInfo.stories);
2668
- const designSystemConfig = await getAppDesignSystemConfig(appPath);
2669
2675
  __agiflowai_aicode_utils.log.info(`[GetUiComponentService] Using theme provider: ${designSystemConfig.themeProvider}`);
2670
2676
  __agiflowai_aicode_utils.log.info(`[GetUiComponentService] Design system type: ${designSystemConfig.type}`);
2671
2677
  const renderer = this.rendererFactory(designSystemConfig, appPath);
@@ -3029,11 +3035,9 @@ var ListSharedComponentsTool = class ListSharedComponentsTool {
3029
3035
  const { cursor, tags: inputTags } = input;
3030
3036
  const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
3031
3037
  const storiesIndex = new StoriesIndexService();
3032
- await storiesIndex.initialize();
3038
+ const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
3033
3039
  const availableTags = storiesIndex.getAllTags();
3034
- let filterTags;
3035
- if (inputTags !== void 0) filterTags = inputTags;
3036
- else filterTags = await getSharedComponentTags();
3040
+ const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
3037
3041
  const allComponentNames = storiesIndex.getComponentsByTags(filterTags.length > 0 ? filterTags : void 0).map((component) => component.title.split("/").pop() || component.title).filter((name, index, self) => self.indexOf(name) === index).sort();
3038
3042
  const totalComponents = allComponentNames.length;
3039
3043
  const paginatedComponents = allComponentNames.slice(offset, offset + ListSharedComponentsTool.PAGE_SIZE);
@@ -694,11 +694,11 @@ var StoriesIndexService = class {
694
694
  log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
695
695
  const failures = [];
696
696
  let successCount = 0;
697
- for (const filePath of storyFiles) try {
698
- await this.indexStoryFile(filePath);
699
- successCount++;
700
- } catch (error) {
701
- const errorMessage = error instanceof Error ? error.message : String(error);
697
+ const results = await Promise.allSettled(storyFiles.map((filePath) => this.indexStoryFile(filePath)));
698
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") successCount++;
699
+ else {
700
+ const filePath = storyFiles[index];
701
+ const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
702
702
  log.error(`[StoriesIndexService] Error indexing ${filePath}: ${errorMessage}`);
703
703
  failures.push({
704
704
  filePath,
@@ -904,12 +904,14 @@ var AppComponentsService = class {
904
904
  } catch {
905
905
  throw new Error(`App path does not exist: ${resolvedAppPath}`);
906
906
  }
907
- const appName = await this.getAppName(resolvedAppPath);
908
- const workspaceDependencies = await this.getWorkspaceDependencies(resolvedAppPath);
909
- log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
910
- const packageMap = await this.buildPackageMap(monorepoRoot);
911
907
  const storiesIndex = new StoriesIndexService();
912
- await storiesIndex.initialize();
908
+ const [appName, workspaceDependencies, packageMap] = await Promise.all([
909
+ this.getAppName(resolvedAppPath),
910
+ this.getWorkspaceDependencies(resolvedAppPath),
911
+ this.buildPackageMap(monorepoRoot),
912
+ storiesIndex.initialize()
913
+ ]);
914
+ log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
913
915
  const allComponents = storiesIndex.getAllComponents();
914
916
  const { appComponentsArray, packageComponents, totalPackageComponents } = this.categorizeComponents(allComponents, resolvedAppPath, workspaceDependencies, packageMap);
915
917
  const totalComponents = appComponentsArray.length + totalPackageComponents;
@@ -987,16 +989,17 @@ var AppComponentsService = class {
987
989
  } catch (error) {
988
990
  throw new Error(`Failed to scan for package.json files in ${monorepoRoot}: ${error instanceof Error ? error.message : String(error)}`);
989
991
  }
990
- for (const pkgJsonPath of packageJsonFiles) try {
992
+ const results = await Promise.allSettled(packageJsonFiles.map(async (pkgJsonPath) => {
991
993
  const content = await promises.readFile(pkgJsonPath, "utf-8");
992
- const pkgJson = JSON.parse(content);
993
- if (pkgJson.name) {
994
- const pkgDir = path.dirname(pkgJsonPath);
995
- packageMap.set(pkgJson.name, pkgDir);
996
- }
997
- } catch (error) {
998
- log.debug(`[AppComponentsService] Skipping invalid package.json at ${pkgJsonPath}:`, error);
999
- }
994
+ return {
995
+ pkgJsonPath,
996
+ name: JSON.parse(content).name
997
+ };
998
+ }));
999
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled" && result.value.name) {
1000
+ const pkgDir = path.dirname(result.value.pkgJsonPath);
1001
+ packageMap.set(result.value.name, pkgDir);
1002
+ } else if (result.status === "rejected") log.debug(`[AppComponentsService] Skipping invalid package.json at ${packageJsonFiles[index]}:`, result.reason);
1000
1003
  return packageMap;
1001
1004
  }
1002
1005
  /**
@@ -2001,13 +2004,12 @@ var CSSThemeService = class extends BaseThemeService {
2001
2004
  const cssFilePaths = this.resolveCSSFilePaths();
2002
2005
  if (cssFilePaths.length === 0) throw new Error("No theme CSS files configured. Set themePath or cssFiles in project.json style-system config.");
2003
2006
  const themes = [];
2004
- for (const cssFilePath of cssFilePaths) try {
2007
+ const results = await Promise.allSettled(cssFilePaths.map(async (cssFilePath) => {
2005
2008
  await this.validatePath(cssFilePath);
2006
- const fileThemes = await this.extractThemesFromCSS(cssFilePath);
2007
- themes.push(...fileThemes);
2008
- } catch (error) {
2009
- log.warn(`[CSSThemeService] Could not process ${cssFilePath}:`, error);
2010
- }
2009
+ return this.extractThemesFromCSS(cssFilePath);
2010
+ }));
2011
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") themes.push(...result.value);
2012
+ else log.warn(`[CSSThemeService] Could not process ${cssFilePaths[index]}:`, result.reason);
2011
2013
  const uniqueThemes = this.deduplicateThemes(themes);
2012
2014
  return {
2013
2015
  themes: uniqueThemes.sort((a, b) => a.name.localeCompare(b.name)),
@@ -2170,13 +2172,14 @@ export default WrappedComponent;
2170
2172
  */
2171
2173
  async getInlineStyles(darkMode = false) {
2172
2174
  const cssFiles = await this.getThemeCSS();
2173
- let styles = "";
2174
- for (const cssFile of cssFiles) try {
2175
- const content = await promises.readFile(cssFile, "utf-8");
2176
- styles += content + "\n";
2177
- } catch (error) {
2178
- log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
2179
- }
2175
+ let styles = (await Promise.all(cssFiles.map(async (cssFile) => {
2176
+ try {
2177
+ return await promises.readFile(cssFile, "utf-8");
2178
+ } catch (error) {
2179
+ log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
2180
+ return "";
2181
+ }
2182
+ }))).filter(Boolean).join("\n");
2180
2183
  if (darkMode && styles) styles = `.dark { ${styles} }`;
2181
2184
  return styles;
2182
2185
  }
@@ -2222,19 +2225,21 @@ export default WrappedComponent;
2222
2225
  const themeFiles = (await promises.readdir(configsPath)).filter((file) => file.endsWith(".json"));
2223
2226
  const themes = [];
2224
2227
  let activeBrand;
2225
- for (const file of themeFiles) {
2228
+ const results = await Promise.allSettled(themeFiles.map(async (file) => {
2226
2229
  const filePath = path.join(configsPath, file);
2227
2230
  const content = await promises.readFile(filePath, "utf-8");
2228
2231
  const themeData = JSON.parse(content);
2229
- const themeName = file.replace(".json", "");
2230
- themes.push({
2231
- name: themeName,
2232
+ return {
2233
+ name: file.replace(".json", ""),
2232
2234
  fileName: file,
2233
2235
  path: filePath,
2234
2236
  colors: themeData.colors || themeData
2235
- });
2236
- if (themeName === "lightTheme" || themeName === "agimonTheme") activeBrand = themeName;
2237
- }
2237
+ };
2238
+ }));
2239
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") {
2240
+ themes.push(result.value);
2241
+ if (result.value.name === "lightTheme" || result.value.name === "agimonTheme") activeBrand = result.value.name;
2242
+ } else log.warn(`[ThemeService] Failed to process theme file ${themeFiles[index]}:`, result.reason);
2238
2243
  return {
2239
2244
  themes: themes.sort((a, b) => a.name.localeCompare(b.name)),
2240
2245
  activeBrand
@@ -2490,44 +2495,44 @@ var ComponentRendererService = class ComponentRendererService {
2490
2495
  }
2491
2496
  }
2492
2497
  /**
2493
- * Maximum number of screenshot files to keep in temp directory.
2498
+ * Default maximum number of screenshot files to keep in temp directory.
2494
2499
  * Prevents disk space issues on long-running servers.
2495
2500
  */
2496
- static MAX_TEMP_FILES = 100;
2501
+ static DEFAULT_MAX_TEMP_FILES = 100;
2502
+ /**
2503
+ * Number of recent files to keep when cleaning up on dispose.
2504
+ */
2505
+ static DISPOSE_KEEP_COUNT = 20;
2497
2506
  /**
2498
2507
  * Clean up old rendered files.
2499
2508
  * Removes files older than the specified duration and enforces a max file count.
2500
2509
  * @param olderThanMs - Remove files older than this duration (default: 1 hour)
2510
+ * @param keepCount - Maximum number of recent files to keep (default: 100)
2501
2511
  */
2502
- async cleanup(olderThanMs = 36e5) {
2512
+ async cleanup(olderThanMs = 36e5, keepCount = ComponentRendererService.DEFAULT_MAX_TEMP_FILES) {
2503
2513
  try {
2504
2514
  const files = await promises.readdir(this.tmpDir);
2505
2515
  const now = Date.now();
2506
2516
  const componentFiles = [];
2507
- for (const file of files) if (file.startsWith("component-")) {
2517
+ const componentFileNames = files.filter((f) => f.startsWith("component-"));
2518
+ const statResults = await Promise.allSettled(componentFileNames.map(async (file) => {
2508
2519
  const filePath = path.join(this.tmpDir, file);
2509
- try {
2510
- const stats = await promises.stat(filePath);
2511
- componentFiles.push({
2512
- name: file,
2513
- path: filePath,
2514
- mtime: stats.mtimeMs
2515
- });
2516
- } catch {}
2517
- }
2518
- let deletedCount = 0;
2519
- for (const file of componentFiles) if (now - file.mtime > olderThanMs) {
2520
- await promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
2521
- deletedCount++;
2522
- }
2520
+ return {
2521
+ name: file,
2522
+ path: filePath,
2523
+ mtime: (await promises.stat(filePath)).mtimeMs
2524
+ };
2525
+ }));
2526
+ for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
2527
+ const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
2528
+ await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
2529
+ let deletedCount = oldFiles.length;
2523
2530
  const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
2524
- if (remainingFiles.length > ComponentRendererService.MAX_TEMP_FILES) {
2531
+ if (remainingFiles.length > keepCount) {
2525
2532
  remainingFiles.sort((a, b) => a.mtime - b.mtime);
2526
- const toDelete = remainingFiles.slice(0, remainingFiles.length - ComponentRendererService.MAX_TEMP_FILES);
2527
- for (const file of toDelete) {
2528
- await promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
2529
- deletedCount++;
2530
- }
2533
+ const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
2534
+ await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
2535
+ deletedCount += toDelete.length;
2531
2536
  }
2532
2537
  if (deletedCount > 0) log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
2533
2538
  } catch (error) {
@@ -2535,12 +2540,13 @@ var ComponentRendererService = class ComponentRendererService {
2535
2540
  }
2536
2541
  }
2537
2542
  /**
2538
- * Cleanup bundler server and temp files.
2543
+ * Cleanup bundler server resources and old temp files.
2539
2544
  * Called on service shutdown.
2545
+ * Keeps the most recent files for caching purposes.
2540
2546
  */
2541
2547
  async dispose() {
2542
2548
  try {
2543
- await this.cleanup(0);
2549
+ await this.cleanup(0, ComponentRendererService.DISPOSE_KEEP_COUNT);
2544
2550
  const bundlerService = this.getBundlerService();
2545
2551
  if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
2546
2552
  } catch (error) {
@@ -2617,16 +2623,16 @@ var GetUiComponentService = class {
2617
2623
  const { componentName, appPath, storyName = this.config.defaultStoryName, darkMode = this.config.defaultDarkMode } = input;
2618
2624
  log.info(`[GetUiComponentService] Starting for component: ${componentName}, appPath: ${appPath}, storyName: ${storyName}`);
2619
2625
  const storiesIndex = this.storiesIndexFactory();
2626
+ let designSystemConfig;
2620
2627
  try {
2621
- await storiesIndex.initialize();
2628
+ [, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
2622
2629
  } catch (error) {
2623
- throw new Error(`Failed to initialize stories index: ${error instanceof Error ? error.message : String(error)}`);
2630
+ throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
2624
2631
  }
2625
2632
  const componentInfo = storiesIndex.findComponentByName(componentName);
2626
2633
  if (!componentInfo) throw new Error(`Component "${componentName}" not found in stories index. Ensure the component has a .stories.tsx file and has been indexed.`);
2627
2634
  log.info(`[GetUiComponentService] Found component: ${componentInfo.title}`);
2628
2635
  const validStoryName = this.resolveStoryName(storyName, componentInfo.stories);
2629
- const designSystemConfig = await getAppDesignSystemConfig(appPath);
2630
2636
  log.info(`[GetUiComponentService] Using theme provider: ${designSystemConfig.themeProvider}`);
2631
2637
  log.info(`[GetUiComponentService] Design system type: ${designSystemConfig.type}`);
2632
2638
  const renderer = this.rendererFactory(designSystemConfig, appPath);
@@ -2990,11 +2996,9 @@ var ListSharedComponentsTool = class ListSharedComponentsTool {
2990
2996
  const { cursor, tags: inputTags } = input;
2991
2997
  const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
2992
2998
  const storiesIndex = new StoriesIndexService();
2993
- await storiesIndex.initialize();
2999
+ const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
2994
3000
  const availableTags = storiesIndex.getAllTags();
2995
- let filterTags;
2996
- if (inputTags !== void 0) filterTags = inputTags;
2997
- else filterTags = await getSharedComponentTags();
3001
+ const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
2998
3002
  const allComponentNames = storiesIndex.getComponentsByTags(filterTags.length > 0 ? filterTags : void 0).map((component) => component.title.split("/").pop() || component.title).filter((name, index, self) => self.indexOf(name) === index).sort();
2999
3003
  const totalComponents = allComponentNames.length;
3000
3004
  const paginatedComponents = allComponentNames.slice(offset, offset + ListSharedComponentsTool.PAGE_SIZE);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agiflowai/style-system",
3
3
  "description": "MCP server for exploring themes, Tailwind CSS classes, and UI components from Storybook with standalone component rendering capabilities",
4
- "version": "0.0.3",
4
+ "version": "0.0.5",
5
5
  "license": "AGPL-3.0",
6
6
  "keywords": [
7
7
  "mcp",
@@ -22,7 +22,7 @@
22
22
  "README.md"
23
23
  ],
24
24
  "dependencies": {
25
- "@modelcontextprotocol/sdk": "1.24.0",
25
+ "@modelcontextprotocol/sdk": "1.25.2",
26
26
  "@storybook/csf-tools": "^8.6.14",
27
27
  "@tailwindcss/vite": "^4.1.16",
28
28
  "chalk": "5.6.2",
@@ -38,7 +38,7 @@
38
38
  "tailwindcss": "^4.1.11",
39
39
  "vite": "^7.1.12",
40
40
  "vite-plugin-singlefile": "^2.3.0",
41
- "@agiflowai/aicode-utils": "1.0.10"
41
+ "@agiflowai/aicode-utils": "1.0.12"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@babel/parser": "^7.28.5",