veryfront 0.1.26 → 0.1.28

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 (124) hide show
  1. package/README.md +3 -11
  2. package/esm/cli/app/shell.d.ts.map +1 -1
  3. package/esm/cli/app/shell.js +9 -5
  4. package/esm/cli/commands/demo/demo.js +1 -1
  5. package/esm/cli/commands/init/catalog.d.ts.map +1 -1
  6. package/esm/cli/commands/init/catalog.js +13 -5
  7. package/esm/cli/commands/init/command-help.js +4 -4
  8. package/esm/cli/commands/init/types.d.ts +1 -1
  9. package/esm/cli/commands/init/types.d.ts.map +1 -1
  10. package/esm/cli/commands/serve/command.d.ts.map +1 -1
  11. package/esm/cli/commands/serve/command.js +0 -4
  12. package/esm/cli/commands/start/command.d.ts.map +1 -1
  13. package/esm/cli/commands/start/command.js +16 -9
  14. package/esm/cli/help/tips.js +6 -6
  15. package/esm/cli/mcp/remote-file-tools.js +1 -1
  16. package/esm/cli/mcp/tools/catalog-tools.d.ts +3 -3
  17. package/esm/cli/mcp/tools/catalog-tools.d.ts.map +1 -1
  18. package/esm/cli/mcp/tools/catalog-tools.js +21 -13
  19. package/esm/cli/mcp/tools/project-tools.js +1 -1
  20. package/esm/cli/templates/index.js +11 -11
  21. package/esm/cli/templates/manifest.d.ts +22 -15
  22. package/esm/cli/templates/manifest.js +24 -17
  23. package/esm/cli/templates/types.d.ts +1 -1
  24. package/esm/cli/templates/types.d.ts.map +1 -1
  25. package/esm/cli/utils/index.d.ts.map +1 -1
  26. package/esm/cli/utils/index.js +13 -1
  27. package/esm/deno.js +1 -1
  28. package/esm/src/html/html-shell-generator.d.ts.map +1 -1
  29. package/esm/src/html/html-shell-generator.js +2 -0
  30. package/esm/src/html/styles-builder/project-css-cache.d.ts +8 -1
  31. package/esm/src/html/styles-builder/project-css-cache.d.ts.map +1 -1
  32. package/esm/src/html/styles-builder/project-css-cache.js +13 -2
  33. package/esm/src/html/styles-builder/tailwind-compiler.d.ts +2 -0
  34. package/esm/src/html/styles-builder/tailwind-compiler.d.ts.map +1 -1
  35. package/esm/src/html/styles-builder/tailwind-compiler.js +52 -19
  36. package/esm/src/modules/react-loader/css-import-collector.d.ts +29 -0
  37. package/esm/src/modules/react-loader/css-import-collector.d.ts.map +1 -0
  38. package/esm/src/modules/react-loader/css-import-collector.js +41 -0
  39. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
  40. package/esm/src/modules/react-loader/ssr-module-loader/loader.js +6 -0
  41. package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.d.ts.map +1 -1
  42. package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.js +5 -0
  43. package/esm/src/platform/adapters/fs/factory.d.ts.map +1 -1
  44. package/esm/src/platform/adapters/fs/factory.js +5 -1
  45. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -0
  46. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
  47. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +19 -5
  48. package/esm/src/platform/compat/process.d.ts.map +1 -1
  49. package/esm/src/platform/compat/process.js +20 -3
  50. package/esm/src/proxy/main.js +31 -12
  51. package/esm/src/proxy/token-manager.d.ts +2 -0
  52. package/esm/src/proxy/token-manager.d.ts.map +1 -1
  53. package/esm/src/proxy/token-manager.js +47 -8
  54. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +23 -0
  55. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -0
  56. package/esm/src/rendering/orchestrator/css-candidate-manifest.js +132 -0
  57. package/esm/src/rendering/orchestrator/html.d.ts +11 -1
  58. package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
  59. package/esm/src/rendering/orchestrator/html.js +103 -18
  60. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  61. package/esm/src/rendering/orchestrator/pipeline.js +14 -2
  62. package/esm/src/server/bootstrap.d.ts +2 -0
  63. package/esm/src/server/bootstrap.d.ts.map +1 -1
  64. package/esm/src/server/bootstrap.js +10 -0
  65. package/esm/src/server/handlers/preview/markdown-html-generator.d.ts.map +1 -1
  66. package/esm/src/server/handlers/preview/markdown-html-generator.js +11 -5
  67. package/esm/src/server/production-server.js +10 -2
  68. package/esm/src/studio/bridge-template.d.ts +2 -0
  69. package/esm/src/studio/bridge-template.d.ts.map +1 -1
  70. package/esm/src/studio/bridge-template.js +3390 -52
  71. package/esm/src/transforms/css-modules/naming.d.ts +33 -0
  72. package/esm/src/transforms/css-modules/naming.d.ts.map +1 -0
  73. package/esm/src/transforms/css-modules/naming.js +128 -0
  74. package/esm/src/transforms/esm/import-parser.d.ts +1 -0
  75. package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
  76. package/esm/src/transforms/esm/import-parser.js +16 -5
  77. package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
  78. package/esm/src/transforms/pipeline/index.js +3 -1
  79. package/esm/src/transforms/pipeline/stages/index.d.ts +1 -0
  80. package/esm/src/transforms/pipeline/stages/index.d.ts.map +1 -1
  81. package/esm/src/transforms/pipeline/stages/index.js +1 -0
  82. package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts +18 -0
  83. package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts.map +1 -0
  84. package/esm/src/transforms/pipeline/stages/ssr-css-strip.js +168 -0
  85. package/package.json +1 -1
  86. package/src/cli/app/shell.ts +9 -5
  87. package/src/cli/commands/demo/demo.ts +1 -1
  88. package/src/cli/commands/init/catalog.ts +13 -5
  89. package/src/cli/commands/init/command-help.ts +4 -4
  90. package/src/cli/commands/init/types.ts +5 -5
  91. package/src/cli/commands/serve/command.ts +0 -5
  92. package/src/cli/commands/start/command.ts +15 -10
  93. package/src/cli/help/tips.ts +6 -6
  94. package/src/cli/mcp/remote-file-tools.ts +1 -1
  95. package/src/cli/mcp/tools/catalog-tools.ts +21 -13
  96. package/src/cli/mcp/tools/project-tools.ts +1 -1
  97. package/src/cli/templates/index.ts +11 -11
  98. package/src/cli/templates/manifest.js +24 -17
  99. package/src/cli/templates/types.ts +5 -5
  100. package/src/cli/utils/index.ts +12 -1
  101. package/src/deno.js +1 -1
  102. package/src/src/html/html-shell-generator.ts +2 -0
  103. package/src/src/html/styles-builder/project-css-cache.ts +24 -1
  104. package/src/src/html/styles-builder/tailwind-compiler.ts +67 -26
  105. package/src/src/modules/react-loader/css-import-collector.ts +50 -0
  106. package/src/src/modules/react-loader/ssr-module-loader/loader.ts +7 -0
  107. package/src/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.ts +6 -0
  108. package/src/src/platform/adapters/fs/factory.ts +5 -1
  109. package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +21 -5
  110. package/src/src/platform/compat/process.ts +28 -4
  111. package/src/src/proxy/main.ts +32 -12
  112. package/src/src/proxy/token-manager.ts +54 -8
  113. package/src/src/rendering/orchestrator/css-candidate-manifest.ts +176 -0
  114. package/src/src/rendering/orchestrator/html.ts +128 -16
  115. package/src/src/rendering/orchestrator/pipeline.ts +183 -165
  116. package/src/src/server/bootstrap.ts +16 -0
  117. package/src/src/server/handlers/preview/markdown-html-generator.ts +12 -5
  118. package/src/src/server/production-server.ts +12 -2
  119. package/src/src/studio/bridge-template.ts +3392 -52
  120. package/src/src/transforms/css-modules/naming.ts +152 -0
  121. package/src/src/transforms/esm/import-parser.ts +15 -5
  122. package/src/src/transforms/pipeline/index.ts +3 -0
  123. package/src/src/transforms/pipeline/stages/index.ts +1 -0
  124. package/src/src/transforms/pipeline/stages/ssr-css-strip.ts +201 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * CSS Module naming and selector rewriting helpers.
3
+ *
4
+ * Provides deterministic class-name scoping that is stable across
5
+ * transform/runtime boundaries and HTML CSS aggregation.
6
+ */
7
+ /**
8
+ * Normalize a module key to a stable slash-based format.
9
+ * Removes query/hash suffixes and normalizes duplicate separators.
10
+ */
11
+ export declare function normalizeCssModuleKey(path: string): string;
12
+ /**
13
+ * Resolve a CSS import specifier to a deterministic module key.
14
+ * Supports relative imports, @/ aliases, absolute paths, and URLs.
15
+ */
16
+ export declare function resolveCssModuleKey(specifier: string, importerFilePath: string, projectDir: string): string;
17
+ /**
18
+ * Build deterministic module scope info.
19
+ */
20
+ export declare function getCssModuleScope(moduleKey: string): {
21
+ base: string;
22
+ hash: string;
23
+ };
24
+ /**
25
+ * Convert a local class name to its scoped CSS Module class.
26
+ */
27
+ export declare function toScopedCssModuleClass(moduleKey: string, localName: string): string;
28
+ /**
29
+ * Rewrite `.module.css` selectors to deterministic scoped class names.
30
+ * Keeps `:global(...)` segments untouched.
31
+ */
32
+ export declare function rewriteCssModuleContent(content: string, moduleKey: string): string;
33
+ //# sourceMappingURL=naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/css-modules/naming.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ1D;AA2BD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,MAAM,CAqBR;AAcD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAUnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAInF;AAsBD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAYlF"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * CSS Module naming and selector rewriting helpers.
3
+ *
4
+ * Provides deterministic class-name scoping that is stable across
5
+ * transform/runtime boundaries and HTML CSS aggregation.
6
+ */
7
+ const CSS_MODULE_EXTENSION = ".module.css";
8
+ /**
9
+ * Normalize a module key to a stable slash-based format.
10
+ * Removes query/hash suffixes and normalizes duplicate separators.
11
+ */
12
+ export function normalizeCssModuleKey(path) {
13
+ const withoutFilePrefix = path.startsWith("file://") ? path.slice("file://".length) : path;
14
+ const withoutQuery = withoutFilePrefix.replace(/[?#].*$/, "");
15
+ const slashed = withoutQuery.replace(/\\/g, "/");
16
+ const collapsed = slashed.replace(/\/{2,}/g, "/");
17
+ if (collapsed.startsWith("/"))
18
+ return collapsed;
19
+ if (collapsed.startsWith("http://") || collapsed.startsWith("https://"))
20
+ return collapsed;
21
+ return `/${collapsed.replace(/^\/+/, "")}`;
22
+ }
23
+ function dirname(path) {
24
+ const normalized = normalizeCssModuleKey(path);
25
+ const lastSlash = normalized.lastIndexOf("/");
26
+ return lastSlash <= 0 ? "/" : normalized.slice(0, lastSlash);
27
+ }
28
+ function normalizePathSegments(path) {
29
+ const normalized = normalizeCssModuleKey(path);
30
+ if (normalized.startsWith("http://") || normalized.startsWith("https://"))
31
+ return normalized;
32
+ const parts = normalized.split("/").filter(Boolean);
33
+ const resolved = [];
34
+ for (const part of parts) {
35
+ if (part === ".")
36
+ continue;
37
+ if (part === "..") {
38
+ resolved.pop();
39
+ continue;
40
+ }
41
+ resolved.push(part);
42
+ }
43
+ return `/${resolved.join("/")}`;
44
+ }
45
+ /**
46
+ * Resolve a CSS import specifier to a deterministic module key.
47
+ * Supports relative imports, @/ aliases, absolute paths, and URLs.
48
+ */
49
+ export function resolveCssModuleKey(specifier, importerFilePath, projectDir) {
50
+ if (specifier.startsWith("http://") || specifier.startsWith("https://")) {
51
+ return normalizeCssModuleKey(specifier);
52
+ }
53
+ if (specifier.startsWith("@/")) {
54
+ const aliasPath = specifier.slice(2).replace(/^\/+/, "");
55
+ return normalizePathSegments(`${normalizeCssModuleKey(projectDir)}/${aliasPath}`);
56
+ }
57
+ if (specifier.startsWith("/")) {
58
+ return normalizePathSegments(specifier);
59
+ }
60
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
61
+ const importerDir = dirname(importerFilePath);
62
+ return normalizePathSegments(`${importerDir}/${specifier}`);
63
+ }
64
+ // Bare specifiers are uncommon for CSS in this system, but keep deterministic behavior.
65
+ return normalizeCssModuleKey(specifier);
66
+ }
67
+ function hashString(input) {
68
+ let hash = 5381;
69
+ for (let i = 0; i < input.length; i++) {
70
+ hash = ((hash << 5) + hash) ^ input.charCodeAt(i);
71
+ }
72
+ return (hash >>> 0).toString(36);
73
+ }
74
+ function sanitizeToken(token) {
75
+ return token.replace(/[^\w-]/g, "_");
76
+ }
77
+ /**
78
+ * Build deterministic module scope info.
79
+ */
80
+ export function getCssModuleScope(moduleKey) {
81
+ const normalized = normalizeCssModuleKey(moduleKey);
82
+ const filename = normalized.split("/").pop() || "module";
83
+ const base = sanitizeToken(filename.endsWith(CSS_MODULE_EXTENSION)
84
+ ? filename.slice(0, -CSS_MODULE_EXTENSION.length)
85
+ : filename.replace(/\.css$/, "")) || "module";
86
+ const hash = hashString(normalized).slice(0, 6);
87
+ return { base, hash };
88
+ }
89
+ /**
90
+ * Convert a local class name to its scoped CSS Module class.
91
+ */
92
+ export function toScopedCssModuleClass(moduleKey, localName) {
93
+ const { base, hash } = getCssModuleScope(moduleKey);
94
+ const normalizedLocal = sanitizeToken(localName);
95
+ return `${base}_${normalizedLocal}__${hash}`;
96
+ }
97
+ function maskGlobalSelectors(css) {
98
+ const segments = [];
99
+ const masked = css.replace(/:global\(([^()]*)\)/g, (match) => {
100
+ const token = `__VF_CSS_GLOBAL_${segments.length}__`;
101
+ segments.push(match);
102
+ return token;
103
+ });
104
+ return {
105
+ masked,
106
+ restore: (input) => {
107
+ let result = input;
108
+ for (const [i, segment] of segments.entries()) {
109
+ result = result.replaceAll(`__VF_CSS_GLOBAL_${i}__`, segment);
110
+ }
111
+ return result;
112
+ },
113
+ };
114
+ }
115
+ /**
116
+ * Rewrite `.module.css` selectors to deterministic scoped class names.
117
+ * Keeps `:global(...)` segments untouched.
118
+ */
119
+ export function rewriteCssModuleContent(content, moduleKey) {
120
+ const { masked, restore } = maskGlobalSelectors(content);
121
+ // After :global() masking, every `.identifier` in the CSS is a local class
122
+ // selector. No lookbehind needed — numeric decimals like `0.5em` won't
123
+ // match because digits aren't in [_a-zA-Z].
124
+ const rewritten = masked.replace(/\.([_a-zA-Z][_a-zA-Z0-9-]*)/g, (_match, className) => {
125
+ return `.${toScopedCssModuleClass(moduleKey, className)}`;
126
+ });
127
+ return restore(rewritten);
128
+ }
@@ -16,6 +16,7 @@ export interface MissingImport {
16
16
  }
17
17
  export interface ParseLocalImportsResult {
18
18
  imports: LocalImport[];
19
+ cssImports: LocalImport[];
19
20
  crossProjectImports: CrossProjectImport[];
20
21
  missing: MissingImport[];
21
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAwElC"}
1
+ {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAiFlC"}
@@ -5,10 +5,10 @@ import { isCrossProjectImport, parseCrossProjectImport } from "./path-resolver.j
5
5
  import { parseImports } from "./lexer.js";
6
6
  import { getLoaderFromPath } from "./transform-utils.js";
7
7
  const EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mdx"];
8
- const HAS_EXTENSION_RE = /\.(tsx?|jsx?|mjs|cjs|mdx)$/;
8
+ const HAS_EXTENSION_RE = /\.(tsx?|jsx?|mjs|cjs|mdx|css)$/;
9
9
  export async function parseLocalImports(code, filePath, projectDir, adapter) {
10
10
  if (filePath.endsWith(".css") || filePath.endsWith(".json")) {
11
- return { imports: [], crossProjectImports: [], missing: [] };
11
+ return { imports: [], cssImports: [], crossProjectImports: [], missing: [] };
12
12
  }
13
13
  const esbuild = await getEsbuild();
14
14
  const result = await esbuild.transform(code, {
@@ -24,6 +24,7 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
24
24
  });
25
25
  const imports = await parseImports(result.code);
26
26
  const localImports = [];
27
+ const cssImports = [];
27
28
  const crossProjectImports = [];
28
29
  const missingImports = [];
29
30
  for (const imp of imports) {
@@ -33,7 +34,12 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
33
34
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
34
35
  const resolved = await resolveLocalImportPath(filePath, specifier, adapter);
35
36
  if (resolved) {
36
- localImports.push({ specifier, absolutePath: resolved });
37
+ if (resolved.endsWith(".css")) {
38
+ cssImports.push({ specifier, absolutePath: resolved });
39
+ }
40
+ else {
41
+ localImports.push({ specifier, absolutePath: resolved });
42
+ }
37
43
  continue;
38
44
  }
39
45
  missingImports.push({
@@ -47,7 +53,12 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
47
53
  const aliasPath = specifier.slice(2);
48
54
  const resolved = await resolveAliasImportPath(aliasPath, projectDir, adapter);
49
55
  if (resolved) {
50
- localImports.push({ specifier, absolutePath: resolved });
56
+ if (resolved.endsWith(".css")) {
57
+ cssImports.push({ specifier, absolutePath: resolved });
58
+ }
59
+ else {
60
+ localImports.push({ specifier, absolutePath: resolved });
61
+ }
51
62
  continue;
52
63
  }
53
64
  missingImports.push({
@@ -69,7 +80,7 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
69
80
  path: parsed.path,
70
81
  });
71
82
  }
72
- return { imports: localImports, crossProjectImports, missing: missingImports };
83
+ return { imports: localImports, cssImports, crossProjectImports, missing: missingImports };
73
84
  }
74
85
  async function checkFileExists(path, adapter) {
75
86
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAyIpB,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,eAAe,CAAC,CAoI1B;AAoBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CASjB;AAkCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,CAEjE;AAED,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,SAAS,EACT,KAAK,EACL,KAAK,EACL,YAAY,GACb,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AA4IpB,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,eAAe,CAAC,CAoI1B;AAoBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CASjB;AAkCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,CAEjE;AAED,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,SAAS,EACT,KAAK,EACL,KAAK,EACL,YAAY,GACb,MAAM,cAAc,CAAC"}
@@ -9,7 +9,7 @@ import { createTransformContext, formatTimingLog, recordStageTiming } from "./co
9
9
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
10
10
  import { computeConfigHash } from "../../cache/config-hash.js";
11
11
  import { computeDepsHash } from "../../cache/dependency-graph.js";
12
- import { compilePlugin, finalizePlugin, parsePlugin, resolveImportsPlugin, ssrHttpCachePlugin, ssrHttpStubPlugin, ssrVfModulesPlugin, } from "./stages/index.js";
12
+ import { compilePlugin, cssStripPlugin, finalizePlugin, parsePlugin, resolveImportsPlugin, ssrHttpCachePlugin, ssrHttpStubPlugin, ssrVfModulesPlugin, } from "./stages/index.js";
13
13
  import { createFileSystem, exists } from "../../platform/compat/fs.js";
14
14
  import { ensureHttpBundlesExist } from "../esm/http-cache.js";
15
15
  import { extractHttpBundlePaths } from "../../modules/react-loader/ssr-module-loader/http-bundle-helpers.js";
@@ -19,6 +19,7 @@ import { extractFrameworkBundlePaths } from "../shared/framework-bundle-paths.js
19
19
  const SSR_PIPELINE = [
20
20
  parsePlugin,
21
21
  compilePlugin,
22
+ cssStripPlugin, // Strip CSS imports before they hit import resolution
22
23
  resolveImportsPlugin, // Unified import resolution
23
24
  ssrVfModulesPlugin, // Resolve /_vf_modules/ to framework files with React transforms
24
25
  ssrHttpStubPlugin,
@@ -28,6 +29,7 @@ const SSR_PIPELINE = [
28
29
  const BROWSER_PIPELINE = [
29
30
  parsePlugin,
30
31
  compilePlugin,
32
+ cssStripPlugin, // Strip CSS imports before they hit import resolution
31
33
  resolveImportsPlugin, // Unified import resolution
32
34
  finalizePlugin,
33
35
  ];
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { parsePlugin } from "./parse.js";
7
7
  export { compilePlugin } from "./compile.js";
8
+ export { cssStripPlugin } from "./ssr-css-strip.js";
8
9
  export { resolveImportsPlugin } from "./resolve-imports.js";
9
10
  export { ssrVfModulesPlugin } from "./ssr-vf-modules.js";
10
11
  export { ssrHttpStubPlugin } from "./ssr-http-stub.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { parsePlugin } from "./parse.js";
7
7
  export { compilePlugin } from "./compile.js";
8
+ export { cssStripPlugin } from "./ssr-css-strip.js";
8
9
  export { resolveImportsPlugin } from "./resolve-imports.js";
9
10
  export { ssrVfModulesPlugin } from "./ssr-vf-modules.js";
10
11
  export { ssrHttpStubPlugin } from "./ssr-http-stub.js";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CSS Strip Stage - removes CSS import statements from compiled code.
3
+ *
4
+ * CSS files are not valid JS modules and will crash both the SSR module
5
+ * loader and browser module system if left in compiled code. This plugin
6
+ * strips them and records the CSS specifiers in pipeline metadata for
7
+ * downstream collection (used by the SSR rendering pipeline to include
8
+ * the CSS content in the HTML output).
9
+ *
10
+ * For CSS Module imports (`import styles from "./X.module.css"`), the
11
+ * import is replaced with a Proxy stub that returns the property name
12
+ * as the class name. This matches the Next.js convention where
13
+ * `styles.container` → `"container"` (identity mapping), which works
14
+ * correctly with Tailwind CSS class-based styling.
15
+ */
16
+ import type { TransformPlugin } from "../types.js";
17
+ export declare const cssStripPlugin: TransformPlugin;
18
+ //# sourceMappingURL=ssr-css-strip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-css-strip.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/ssr-css-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA2JnD,eAAO,MAAM,cAAc,EAAE,eA6B5B,CAAC"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * CSS Strip Stage - removes CSS import statements from compiled code.
3
+ *
4
+ * CSS files are not valid JS modules and will crash both the SSR module
5
+ * loader and browser module system if left in compiled code. This plugin
6
+ * strips them and records the CSS specifiers in pipeline metadata for
7
+ * downstream collection (used by the SSR rendering pipeline to include
8
+ * the CSS content in the HTML output).
9
+ *
10
+ * For CSS Module imports (`import styles from "./X.module.css"`), the
11
+ * import is replaced with a Proxy stub that returns the property name
12
+ * as the class name. This matches the Next.js convention where
13
+ * `styles.container` → `"container"` (identity mapping), which works
14
+ * correctly with Tailwind CSS class-based styling.
15
+ */
16
+ import { TransformStage } from "../types.js";
17
+ import { parseImports, rewriteImports } from "../../esm/lexer.js";
18
+ import { getCssModuleScope, resolveCssModuleKey, toScopedCssModuleClass, } from "../../css-modules/naming.js";
19
+ function isCSSImport(specifier) {
20
+ return specifier?.endsWith(".css") || false;
21
+ }
22
+ function isCssModuleImport(specifier) {
23
+ return specifier?.endsWith(".module.css") || false;
24
+ }
25
+ function cssModuleProxyExpression() {
26
+ return "new Proxy({}, { get: (_, p) => String(p) })";
27
+ }
28
+ function scopedCssModuleProxyExpression(moduleKey) {
29
+ const scope = getCssModuleScope(moduleKey);
30
+ return `new Proxy({}, { get: (_, p) => typeof p === "string" ? "${scope.base}_" + String(p).replace(/[^\\w-]/g, "_") + "__${scope.hash}" : "" })`;
31
+ }
32
+ function parseNamedImportBindings(namedClause) {
33
+ const bindings = [];
34
+ for (const rawPart of namedClause.split(",")) {
35
+ const part = rawPart.trim();
36
+ if (!part)
37
+ continue;
38
+ const aliasMatch = part.match(/^([_$a-zA-Z][\w$-]*)\s+as\s+([_$a-zA-Z][\w$]*)$/);
39
+ if (aliasMatch) {
40
+ const imported = aliasMatch[1];
41
+ const local = aliasMatch[2];
42
+ if (!imported || !local)
43
+ continue;
44
+ bindings.push({ imported, local });
45
+ continue;
46
+ }
47
+ if (/^[_$a-zA-Z][\w$]*$/.test(part)) {
48
+ bindings.push({ imported: part, local: part });
49
+ }
50
+ }
51
+ return bindings;
52
+ }
53
+ /**
54
+ * Generate a replacement for a static CSS import statement.
55
+ *
56
+ * - Side-effect import: `import "./globals.css"` → comment
57
+ * - Default import: `import styles from "./X.module.css"` → Proxy stub
58
+ * - Named imports: `import { a } from "./X.css"` → null stubs
59
+ */
60
+ function generateCSSStub(statement, specifier) {
61
+ const trimmed = statement.trim();
62
+ // Re-export from CSS: export { default as styles } from './module.css'
63
+ // → strip entirely, the CSS is collected separately
64
+ if (/^export\s/.test(trimmed)) {
65
+ return `/* css re-export stripped: ${specifier} */`;
66
+ }
67
+ // Side-effect import: import "./globals.css"
68
+ if (/^import\s+['"`]/.test(trimmed)) {
69
+ return `/* css import: ${specifier} */`;
70
+ }
71
+ const fromIndex = trimmed.lastIndexOf(" from ");
72
+ if (fromIndex === -1) {
73
+ return `/* css import: ${specifier} */`;
74
+ }
75
+ const cssModuleKey = isCssModuleImport(specifier) ? specifier : undefined;
76
+ const importClause = trimmed.slice(6, fromIndex).trim(); // Skip "import "
77
+ // Default import: import styles from "./Button.module.css"
78
+ // → const styles = new Proxy({}, { get: (_, p) => String(p) })
79
+ // This makes styles.container return "container" (identity mapping)
80
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(importClause)) {
81
+ const expr = cssModuleKey
82
+ ? scopedCssModuleProxyExpression(cssModuleKey)
83
+ : cssModuleProxyExpression();
84
+ return `const ${importClause} = ${expr}; /* css module: ${specifier} */`;
85
+ }
86
+ // Namespace import: import * as styles from "./X.module.css"
87
+ const nsMatch = importClause.match(/^\*\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)$/);
88
+ if (nsMatch) {
89
+ const expr = cssModuleKey
90
+ ? scopedCssModuleProxyExpression(cssModuleKey)
91
+ : cssModuleProxyExpression();
92
+ return `const ${nsMatch[1]} = ${expr}; /* css module: ${specifier} */`;
93
+ }
94
+ // Named imports: import { container, header } from "./X.module.css"
95
+ const namedMatch = importClause.match(/^\{([^}]+)\}$/);
96
+ if (namedMatch?.[1]) {
97
+ const bindings = parseNamedImportBindings(namedMatch[1]);
98
+ if (bindings.length > 0) {
99
+ const stubs = bindings
100
+ .map((binding) => {
101
+ const value = cssModuleKey
102
+ ? toScopedCssModuleClass(cssModuleKey, binding.imported)
103
+ : binding.imported;
104
+ return `${binding.local} = "${value}"`;
105
+ })
106
+ .join(", ");
107
+ return `const ${stubs}; /* css module: ${specifier} */`;
108
+ }
109
+ }
110
+ // Mixed: import styles, { container } from "./X.module.css"
111
+ const mixedMatch = importClause.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*\{([^}]+)\}$/);
112
+ if (mixedMatch?.[1] && mixedMatch[2]) {
113
+ const defaultName = mixedMatch[1];
114
+ const bindings = parseNamedImportBindings(mixedMatch[2]);
115
+ const namedStubs = bindings
116
+ .map((binding) => {
117
+ const value = cssModuleKey
118
+ ? toScopedCssModuleClass(cssModuleKey, binding.imported)
119
+ : binding.imported;
120
+ return `${binding.local} = "${value}"`;
121
+ })
122
+ .join(", ");
123
+ const defaultExpr = cssModuleKey
124
+ ? scopedCssModuleProxyExpression(cssModuleKey)
125
+ : cssModuleProxyExpression();
126
+ return namedStubs.length > 0
127
+ ? `const ${defaultName} = ${defaultExpr}, ${namedStubs}; /* css module: ${specifier} */`
128
+ : `const ${defaultName} = ${defaultExpr}; /* css module: ${specifier} */`;
129
+ }
130
+ return `/* css import: ${specifier} */`;
131
+ }
132
+ /**
133
+ * Generate a replacement for dynamic CSS imports.
134
+ * Keeps syntax valid in expression position (e.g. await import("./x.css")).
135
+ */
136
+ function generateDynamicCSSStub(specifier) {
137
+ if (isCssModuleImport(specifier)) {
138
+ return `Promise.resolve({ default: ${scopedCssModuleProxyExpression(specifier)} }) /* css import: ${specifier} */`;
139
+ }
140
+ return `Promise.resolve({}) /* css import: ${specifier} */`;
141
+ }
142
+ export const cssStripPlugin = {
143
+ name: "css-strip",
144
+ stage: TransformStage.COMPILE + 0.5, // Run after esbuild compile, before import resolution
145
+ async transform(ctx) {
146
+ const imports = await parseImports(ctx.code);
147
+ const hasCssImports = imports.some((imp) => isCSSImport(imp.n));
148
+ if (!hasCssImports)
149
+ return ctx.code;
150
+ const cssSpecifiers = [];
151
+ const result = await rewriteImports(ctx.code, (imp, statement) => {
152
+ if (!isCSSImport(imp.n))
153
+ return null;
154
+ cssSpecifiers.push(imp.n);
155
+ const moduleKey = isCssModuleImport(imp.n)
156
+ ? resolveCssModuleKey(imp.n, ctx.filePath, ctx.projectDir)
157
+ : undefined;
158
+ const specifierForStub = moduleKey ?? imp.n;
159
+ if (imp.d > -1)
160
+ return generateDynamicCSSStub(specifierForStub);
161
+ return generateCSSStub(statement, specifierForStub);
162
+ });
163
+ if (cssSpecifiers.length > 0) {
164
+ ctx.metadata.set("cssImports", cssSpecifiers);
165
+ }
166
+ return result;
167
+ },
168
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -98,12 +98,16 @@ export function createApp(config: AppConfig): App {
98
98
  }
99
99
 
100
100
  state = setTemplates([
101
- { id: "chat", name: "AI Chatbot", description: "Agent + chat UI + streaming" },
102
- { id: "rag", name: "Chat with Docs", description: "RAG with source citations" },
103
- { id: "multi-agent", name: "Multi-Agent", description: "Agents that delegate to each other" },
104
- { id: "workflow", name: "AI Workflow", description: "Steps + approvals + parallelism" },
101
+ { id: "ai-agent", name: "AI Chatbot", description: "Agent + chat UI + streaming" },
102
+ { id: "chat-with-your-docs", name: "Chat with Docs", description: "RAG with source citations" },
103
+ {
104
+ id: "multi-agent-system",
105
+ name: "Multi-Agent",
106
+ description: "Agents that delegate to each other",
107
+ },
108
+ { id: "agentic-workflow", name: "AI Workflow", description: "Steps + approvals + parallelism" },
105
109
  { id: "coding-agent", name: "Coding Agent", description: "AI code assistant with file tools" },
106
- { id: "saas", name: "AI SaaS", description: "Auth + chat + per-user memory" },
110
+ { id: "saas-starter", name: "AI SaaS", description: "Auth + chat + per-user memory" },
107
111
  { id: "minimal", name: "Minimal", description: "Blank canvas" },
108
112
  ])(state);
109
113
 
@@ -329,7 +329,7 @@ async function executeStepAction(
329
329
  case "create": {
330
330
  await initCommand({
331
331
  name: projectName,
332
- template: "chat",
332
+ template: "ai-agent",
333
333
  quiet: true,
334
334
  skipInstall: true,
335
335
  skipEnvPrompt: true,
@@ -19,16 +19,24 @@ export interface TemplateOption {
19
19
 
20
20
  export const TEMPLATES: readonly TemplateOption[] = [
21
21
  { id: "minimal", label: "Minimal", description: "Blank canvas, no extras" },
22
- { id: "chat", label: "AI Chatbot", description: "Agent + chat UI + streaming" },
23
- { id: "rag", label: "Chat with Your Docs", description: "RAG with source citations" },
24
- { id: "workflow", label: "AI Workflow Pipeline", description: "Steps + approvals + parallelism" },
22
+ { id: "ai-agent", label: "AI Agent", description: "Agent + chat UI + streaming" },
25
23
  {
26
- id: "multi-agent",
24
+ id: "chat-with-your-docs",
25
+ label: "Chat with Your Docs",
26
+ description: "RAG with source citations",
27
+ },
28
+ {
29
+ id: "agentic-workflow",
30
+ label: "Agentic Workflow",
31
+ description: "Steps + approvals + parallelism",
32
+ },
33
+ {
34
+ id: "multi-agent-system",
27
35
  label: "Multi-Agent System",
28
36
  description: "Agents that delegate to each other",
29
37
  },
30
38
  { id: "coding-agent", label: "Coding Agent", description: "AI code assistant with file tools" },
31
- { id: "saas", label: "AI SaaS", description: "Auth + chat + per-user memory" },
39
+ { id: "saas-starter", label: "SaaS Starter", description: "Auth + chat + per-user memory" },
32
40
  ] as const;
33
41
 
34
42
  /** Get templates as SelectOption[] for terminal-select */
@@ -8,7 +8,7 @@ export const initHelp: CommandHelp = {
8
8
  {
9
9
  flag: "-t, --template <name>",
10
10
  description:
11
- "Project template (minimal | chat | rag | workflow | multi-agent | coding-agent | saas)",
11
+ "Project template (minimal | ai-agent | chat-with-your-docs | agentic-workflow | multi-agent-system | coding-agent | saas-starter)",
12
12
  },
13
13
  {
14
14
  flag: "--integrations <list>",
@@ -38,9 +38,9 @@ export const initHelp: CommandHelp = {
38
38
  examples: [
39
39
  "veryfront init # Interactive wizard",
40
40
  "veryfront init my-app",
41
- "veryfront init my-app --template chat",
42
- "veryfront init my-rag --template rag",
43
- "veryfront init my-pipeline --template workflow",
41
+ "veryfront init my-app --template ai-agent",
42
+ "veryfront init my-rag --template chat-with-your-docs",
43
+ "veryfront init my-pipeline --template agentic-workflow",
44
44
  "veryfront init my-app --deploy # Create and deploy",
45
45
  "veryfront init --config project.json # From config file",
46
46
  ],
@@ -1,12 +1,12 @@
1
1
  import type { FeatureName, IntegrationName } from "../../templates/types.js";
2
2
 
3
3
  export type InitTemplate =
4
- | "chat"
5
- | "rag"
6
- | "multi-agent"
7
- | "workflow"
4
+ | "ai-agent"
5
+ | "chat-with-your-docs"
6
+ | "multi-agent-system"
7
+ | "agentic-workflow"
8
8
  | "coding-agent"
9
- | "saas"
9
+ | "saas-starter"
10
10
  | "minimal";
11
11
 
12
12
  export type EnvValues = Record<string, string>;
@@ -37,11 +37,6 @@ async function runProxy(options: ServeOptions): Promise<void> {
37
37
  setEnv("PORT", String(options.port));
38
38
  setEnv("HOST", options.bindAddress);
39
39
 
40
- registerTerminationSignals((signal) => {
41
- cliLogger.info(`Received ${signal}, shutting down proxy server...`);
42
- exitProcess(0);
43
- });
44
-
45
40
  // DenoHttpServer.serve() blocks until the server stops,
46
41
  // so this import keeps the process alive.
47
42
  await import("../../../src/proxy/main.js");
@@ -233,22 +233,27 @@ export async function startCommand(options: StartOptions): Promise<void> {
233
233
  app.setServerReady();
234
234
 
235
235
  let shuttingDown = false;
236
- async function shutdown(): Promise<void> {
236
+ async function shutdown(signal: "SIGINT" | "SIGTERM"): Promise<void> {
237
237
  if (shuttingDown) return;
238
238
  shuttingDown = true;
239
239
 
240
240
  restoreConsole();
241
- cliLogger.info("Shutting down...");
242
-
243
- app.stop();
244
- await mcpServer.stop();
245
- shutdownController.abort();
246
- await server.stop();
247
- await proxy.close();
248
- exitProcess(0);
241
+ cliLogger.info(`Received ${signal}, shutting down...`);
242
+
243
+ try {
244
+ app.stop();
245
+ await mcpServer.stop();
246
+ shutdownController.abort();
247
+ await server.stop();
248
+ await proxy.close();
249
+ } catch (error) {
250
+ cliLogger.warn("Error while shutting down start command:", error);
251
+ } finally {
252
+ exitProcess(0);
253
+ }
249
254
  }
250
255
 
251
- registerTerminationSignals(() => void shutdown());
256
+ registerTerminationSignals((signal) => shutdown(signal));
252
257
  app.start();
253
258
 
254
259
  await new Promise(() => {});