frontmcp 0.6.0 → 0.6.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.
package/README.md CHANGED
@@ -14,6 +14,7 @@ _Made with ❤️ for TypeScript developers_
14
14
  [![NPM - @frontmcp/sdk](https://img.shields.io/npm/v/@frontmcp/sdk.svg?v=2)](https://www.npmjs.com/package/@frontmcp/sdk)
15
15
  [![Node](https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=white)](https://nodejs.org)
16
16
  [![License](https://img.shields.io/github/license/agentfront/frontmcp.svg?v=1)](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
17
+ [![Snyk](https://snyk.io/test/github/agentfront/frontmcp/badge.svg)](https://snyk.io/test/github/agentfront/frontmcp)
17
18
 
18
19
  </div>
19
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -26,13 +26,14 @@
26
26
  "frontmcp": "./src/cli.js"
27
27
  },
28
28
  "dependencies": {
29
- "tslib": "^2.3.0"
29
+ "tslib": "^2.3.0",
30
+ "@rspack/core": "^1.3.12"
30
31
  },
31
32
  "devDependencies": {
32
33
  "typescript": "^5.5.3",
33
34
  "tsx": "^4.20.6",
34
35
  "@types/node": "^24.0.0",
35
- "@modelcontextprotocol/inspector": "^0.17.2"
36
+ "@modelcontextprotocol/inspector": "^0.18.0"
36
37
  },
37
38
  "types": "./src/index.d.ts",
38
39
  "type": "commonjs"
@@ -1,11 +1,17 @@
1
1
  import { AdapterTemplate } from '../types';
2
2
  /**
3
3
  * AWS Lambda adapter - serverless deployment on AWS Lambda.
4
- * Compiles to ESM and uses @codegenie/serverless-express to wrap the Express app.
4
+ * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
5
5
  *
6
6
  * Prerequisites:
7
7
  * npm install @codegenie/serverless-express
8
8
  *
9
+ * The build process:
10
+ * 1. TypeScript compiles to ESM in dist/
11
+ * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
12
+ * 3. index.js imports setup first, then main module
13
+ * 4. rspack bundles everything into handler.cjs
14
+ *
9
15
  * @see https://github.com/codegenie/serverless-express
10
16
  */
11
17
  export declare const lambdaAdapter: AdapterTemplate;
@@ -3,23 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.lambdaAdapter = void 0;
4
4
  /**
5
5
  * AWS Lambda adapter - serverless deployment on AWS Lambda.
6
- * Compiles to ESM and uses @codegenie/serverless-express to wrap the Express app.
6
+ * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
7
7
  *
8
8
  * Prerequisites:
9
9
  * npm install @codegenie/serverless-express
10
10
  *
11
+ * The build process:
12
+ * 1. TypeScript compiles to ESM in dist/
13
+ * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
14
+ * 3. index.js imports setup first, then main module
15
+ * 4. rspack bundles everything into handler.cjs
16
+ *
11
17
  * @see https://github.com/codegenie/serverless-express
12
18
  */
13
19
  exports.lambdaAdapter = {
14
20
  moduleFormat: 'esnext',
21
+ shouldBundle: true,
22
+ bundleOutput: 'handler.cjs',
23
+ getSetupTemplate: () => `// Serverless environment setup - MUST be imported first
24
+ // This sets FRONTMCP_SERVERLESS before any decorators run
25
+ // Required because ESM hoists imports before other statements
26
+ process.env.FRONTMCP_SERVERLESS = '1';
27
+ `,
15
28
  getEntryTemplate: (mainModulePath) => `// Auto-generated AWS Lambda entry point
16
29
  // Generated by: frontmcp build --adapter lambda
17
30
  //
18
31
  // IMPORTANT: This adapter requires @codegenie/serverless-express
19
32
  // Install it with: npm install @codegenie/serverless-express
20
33
  //
21
- process.env.FRONTMCP_SERVERLESS = '1';
22
-
34
+ import './serverless-setup.js';
23
35
  import '${mainModulePath}';
24
36
  import { getServerlessHandlerAsync } from '@frontmcp/sdk';
25
37
  import serverlessExpress from '@codegenie/serverless-express';
@@ -1 +1 @@
1
- {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;GAQG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IAEtB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;;UAQtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM and uses @codegenie/serverless-express to wrap the Express app.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --adapter lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nprocess.env.FRONTMCP_SERVERLESS = '1';\n\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
1
+ {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --adapter lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
@@ -1,7 +1,13 @@
1
1
  import { AdapterTemplate } from '../types';
2
2
  /**
3
3
  * Vercel adapter - serverless deployment on Vercel.
4
- * Compiles to ESM and generates a handler that exports the Express app.
4
+ * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
5
+ *
6
+ * The build process:
7
+ * 1. TypeScript compiles to ESM in dist/
8
+ * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
9
+ * 3. index.js imports setup first, then main module
10
+ * 4. rspack bundles everything into handler.cjs
5
11
  *
6
12
  * @see https://vercel.com/docs/frameworks/express
7
13
  */
@@ -3,16 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.vercelAdapter = void 0;
4
4
  /**
5
5
  * Vercel adapter - serverless deployment on Vercel.
6
- * Compiles to ESM and generates a handler that exports the Express app.
6
+ * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
7
+ *
8
+ * The build process:
9
+ * 1. TypeScript compiles to ESM in dist/
10
+ * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
11
+ * 3. index.js imports setup first, then main module
12
+ * 4. rspack bundles everything into handler.cjs
7
13
  *
8
14
  * @see https://vercel.com/docs/frameworks/express
9
15
  */
10
16
  exports.vercelAdapter = {
11
17
  moduleFormat: 'esnext',
18
+ shouldBundle: true,
19
+ bundleOutput: 'handler.cjs',
20
+ getSetupTemplate: () => `// Serverless environment setup - MUST be imported first
21
+ // This sets FRONTMCP_SERVERLESS before any decorators run
22
+ // Required because ESM hoists imports before other statements
23
+ process.env.FRONTMCP_SERVERLESS = '1';
24
+ `,
12
25
  getEntryTemplate: (mainModulePath) => `// Auto-generated Vercel entry point
13
26
  // Generated by: frontmcp build --adapter vercel
14
- process.env.FRONTMCP_SERVERLESS = '1';
15
-
27
+ import './serverless-setup.js';
16
28
  import '${mainModulePath}';
17
29
  import { getServerlessHandlerAsync } from '@frontmcp/sdk';
18
30
 
@@ -28,8 +40,8 @@ export default async function handler(req, res) {
28
40
  `,
29
41
  getConfig: () => ({
30
42
  version: 2,
31
- builds: [{ src: 'dist/index.js', use: '@vercel/node' }],
32
- routes: [{ src: '/(.*)', dest: '/dist/index.js' }],
43
+ builds: [{ src: 'dist/handler.cjs', use: '@vercel/node' }],
44
+ routes: [{ src: '/(.*)', dest: '/dist/handler.cjs' }],
33
45
  }),
34
46
  configFileName: 'vercel.json',
35
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IAEtB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;UAItC,cAAc;;;;;;;;;;;;CAYvB;IAEC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC;QACvD,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;KACnD,CAAC;IAEF,cAAc,EAAE,aAAa;CAC9B,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM and generates a handler that exports the Express app.\n *\n * @see https://vercel.com/docs/frameworks/express\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --adapter vercel\nprocess.env.FRONTMCP_SERVERLESS = '1';\n\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n getConfig: () => ({\n version: 2,\n builds: [{ src: 'dist/index.js', use: '@vercel/node' }],\n routes: [{ src: '/(.*)', dest: '/dist/index.js' }],\n }),\n\n configFileName: 'vercel.json',\n};\n"]}
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;UAGtC,cAAc;;;;;;;;;;;;CAYvB;IAEC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC;QAC1D,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;KACtD,CAAC;IAEF,cAAc,EAAE,aAAa;CAC9B,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://vercel.com/docs/frameworks/express\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --adapter vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n getConfig: () => ({\n version: 2,\n builds: [{ src: 'dist/handler.cjs', use: '@vercel/node' }],\n routes: [{ src: '/(.*)', dest: '/dist/handler.cjs' }],\n }),\n\n configFileName: 'vercel.json',\n};\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Bundle the serverless entry point into a single CJS file using rspack.
3
+ * This resolves ESM/CJS compatibility issues and dynamic import problems.
4
+ *
5
+ * @param entryPath - Absolute path to the entry file (e.g., dist/index.js)
6
+ * @param outDir - Output directory for the bundled file
7
+ * @param outputFilename - Name of the output bundle (e.g., 'handler.cjs')
8
+ */
9
+ export declare function bundleForServerless(entryPath: string, outDir: string, outputFilename: string): Promise<void>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bundleForServerless = bundleForServerless;
4
+ const core_1 = require("@rspack/core");
5
+ const colors_1 = require("../../colors");
6
+ /**
7
+ * Bundle the serverless entry point into a single CJS file using rspack.
8
+ * This resolves ESM/CJS compatibility issues and dynamic import problems.
9
+ *
10
+ * @param entryPath - Absolute path to the entry file (e.g., dist/index.js)
11
+ * @param outDir - Output directory for the bundled file
12
+ * @param outputFilename - Name of the output bundle (e.g., 'handler.cjs')
13
+ */
14
+ async function bundleForServerless(entryPath, outDir, outputFilename) {
15
+ const compiler = (0, core_1.rspack)({
16
+ mode: 'production',
17
+ target: 'node',
18
+ entry: entryPath,
19
+ output: {
20
+ path: outDir,
21
+ filename: outputFilename,
22
+ library: { type: 'commonjs2' },
23
+ clean: false,
24
+ },
25
+ // Use node externals preset for built-in modules
26
+ externalsPresets: { node: true },
27
+ // Exclude problematic optional dependencies
28
+ externals: {
29
+ '@swc/core': '@swc/core',
30
+ fsevents: 'fsevents',
31
+ esbuild: 'esbuild',
32
+ },
33
+ resolve: {
34
+ extensions: ['.js', '.mjs', '.cjs', '.json'],
35
+ },
36
+ // Don't minimize to preserve readability for debugging
37
+ optimization: {
38
+ minimize: false,
39
+ },
40
+ // Suppress verbose output
41
+ stats: 'errors-warnings',
42
+ });
43
+ return new Promise((resolve, reject) => {
44
+ compiler.run((err, stats) => {
45
+ if (err) {
46
+ return reject(err);
47
+ }
48
+ if (stats?.hasErrors()) {
49
+ const info = stats.toJson();
50
+ const errorMessages = info.errors?.map((e) => e.message).join('\n') || 'Unknown error';
51
+ return reject(new Error(`Bundle failed:\n${errorMessages}`));
52
+ }
53
+ if (stats?.hasWarnings()) {
54
+ const info = stats.toJson();
55
+ info.warnings?.forEach((w) => {
56
+ console.log((0, colors_1.c)('yellow', ` Warning: ${w.message}`));
57
+ });
58
+ }
59
+ compiler.close((closeErr) => {
60
+ if (closeErr) {
61
+ console.log((0, colors_1.c)('yellow', ` Warning closing compiler: ${closeErr.message}`));
62
+ }
63
+ resolve();
64
+ });
65
+ });
66
+ });
67
+ }
68
+ //# sourceMappingURL=bundler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler.js","sourceRoot":"","sources":["../../../../src/commands/build/bundler.ts"],"names":[],"mappings":";;AAWA,kDA0DC;AArED,uCAAsC;AACtC,yCAAiC;AAEjC;;;;;;;GAOG;AACI,KAAK,UAAU,mBAAmB,CACvC,SAAiB,EACjB,MAAc,EACd,cAAsB;IAEtB,MAAM,QAAQ,GAAG,IAAA,aAAM,EAAC;QACtB,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE;YACN,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,cAAc;YACxB,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC9B,KAAK,EAAE,KAAK;SACb;QACD,iDAAiD;QACjD,gBAAgB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;QAChC,4CAA4C;QAC5C,SAAS,EAAE;YACT,WAAW,EAAE,WAAW;YACxB,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,SAAS;SACnB;QACD,OAAO,EAAE;YACP,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;SAC7C;QACD,uDAAuD;QACvD,YAAY,EAAE;YACZ,QAAQ,EAAE,KAAK;SAChB;QACD,0BAA0B;QAC1B,KAAK,EAAE,iBAAiB;KACzB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YAC1B,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YACD,IAAI,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC5B,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC;gBACvF,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,aAAa,EAAE,CAAC,CAAC,CAAC;YAC/D,CAAC;YACD,IAAI,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC5B,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBAC3B,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACtD,CAAC,CAAC,CAAC;YACL,CAAC;YACD,QAAQ,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE;gBAC1B,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,QAAQ,EAAE,+BAA+B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC9E,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { rspack } from '@rspack/core';\nimport { c } from '../../colors';\n\n/**\n * Bundle the serverless entry point into a single CJS file using rspack.\n * This resolves ESM/CJS compatibility issues and dynamic import problems.\n *\n * @param entryPath - Absolute path to the entry file (e.g., dist/index.js)\n * @param outDir - Output directory for the bundled file\n * @param outputFilename - Name of the output bundle (e.g., 'handler.cjs')\n */\nexport async function bundleForServerless(\n entryPath: string,\n outDir: string,\n outputFilename: string,\n): Promise<void> {\n const compiler = rspack({\n mode: 'production',\n target: 'node',\n entry: entryPath,\n output: {\n path: outDir,\n filename: outputFilename,\n library: { type: 'commonjs2' },\n clean: false,\n },\n // Use node externals preset for built-in modules\n externalsPresets: { node: true },\n // Exclude problematic optional dependencies\n externals: {\n '@swc/core': '@swc/core',\n fsevents: 'fsevents',\n esbuild: 'esbuild',\n },\n resolve: {\n extensions: ['.js', '.mjs', '.cjs', '.json'],\n },\n // Don't minimize to preserve readability for debugging\n optimization: {\n minimize: false,\n },\n // Suppress verbose output\n stats: 'errors-warnings',\n });\n\n return new Promise((resolve, reject) => {\n compiler.run((err, stats) => {\n if (err) {\n return reject(err);\n }\n if (stats?.hasErrors()) {\n const info = stats.toJson();\n const errorMessages = info.errors?.map((e) => e.message).join('\\n') || 'Unknown error';\n return reject(new Error(`Bundle failed:\\n${errorMessages}`));\n }\n if (stats?.hasWarnings()) {\n const info = stats.toJson();\n info.warnings?.forEach((w) => {\n console.log(c('yellow', ` Warning: ${w.message}`));\n });\n }\n compiler.close((closeErr) => {\n if (closeErr) {\n console.log(c('yellow', ` Warning closing compiler: ${closeErr.message}`));\n }\n resolve();\n });\n });\n });\n}\n"]}
@@ -7,6 +7,7 @@ const colors_1 = require("../../colors");
7
7
  const fs_1 = require("../../utils/fs");
8
8
  const tsconfig_1 = require("../../tsconfig");
9
9
  const adapters_1 = require("./adapters");
10
+ const bundler_1 = require("./bundler");
10
11
  function isTsLike(p) {
11
12
  return /\.tsx?$/i.test(p);
12
13
  }
@@ -15,6 +16,14 @@ function isTsLike(p) {
15
16
  */
16
17
  async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
17
18
  const template = adapters_1.ADAPTERS[adapter];
19
+ // Generate serverless setup file first (if adapter has one)
20
+ // This file sets FRONTMCP_SERVERLESS=1 before any imports run
21
+ if (template.getSetupTemplate) {
22
+ const setupContent = template.getSetupTemplate();
23
+ const setupPath = path.join(outDir, 'serverless-setup.js');
24
+ await fs_1.fsp.writeFile(setupPath, setupContent, 'utf8');
25
+ console.log((0, colors_1.c)('green', ` Generated serverless setup at ${path.relative(cwd, setupPath)}`));
26
+ }
18
27
  // Generate index.js entry point
19
28
  const mainModuleName = entryBasename.replace(/\.tsx?$/, '.js');
20
29
  const entryContent = template.getEntryTemplate(`./${mainModuleName}`);
@@ -24,6 +33,13 @@ async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
24
33
  await fs_1.fsp.writeFile(entryPath, entryContent, 'utf8');
25
34
  console.log((0, colors_1.c)('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));
26
35
  }
36
+ // Bundle if adapter requires it (creates single CJS file for serverless)
37
+ if (template.shouldBundle && template.bundleOutput) {
38
+ console.log((0, colors_1.c)('cyan', `[build] Bundling for ${adapter}...`));
39
+ const entryPath = path.join(outDir, 'index.js');
40
+ await (0, bundler_1.bundleForServerless)(entryPath, outDir, template.bundleOutput);
41
+ console.log((0, colors_1.c)('green', ` Created bundle: ${template.bundleOutput}`));
42
+ }
27
43
  // Generate config file if adapter has one (skip if already exists)
28
44
  if (template.getConfig && template.configFileName) {
29
45
  const configPath = path.join(cwd, template.configFileName);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/build/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { ParsedArgs } from '../../args';\nimport { c } from '../../colors';\nimport { ensureDir, fileExists, fsp, runCmd, resolveEntry, writeJSON } from '../../utils/fs';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../tsconfig';\nimport { ADAPTERS } from './adapters';\nimport { AdapterName } from './types';\n\nfunction isTsLike(p: string): boolean {\n return /\\.tsx?$/i.test(p);\n}\n\n/**\n * Generate adapter-specific entry point and config files.\n */\nasync function generateAdapterFiles(\n adapter: AdapterName,\n outDir: string,\n entryBasename: string,\n cwd: string,\n): Promise<void> {\n const template = ADAPTERS[adapter];\n\n // Generate index.js entry point\n const mainModuleName = entryBasename.replace(/\\.tsx?$/, '.js');\n const entryContent = template.getEntryTemplate(`./${mainModuleName}`);\n\n // Skip if no entry template (e.g., node adapter)\n if (entryContent) {\n const entryPath = path.join(outDir, 'index.js');\n await fsp.writeFile(entryPath, entryContent, 'utf8');\n console.log(c('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));\n }\n\n // Generate config file if adapter has one (skip if already exists)\n if (template.getConfig && template.configFileName) {\n const configPath = path.join(cwd, template.configFileName);\n\n if (await fileExists(configPath)) {\n console.log(c('yellow', ` ${template.configFileName} already exists (skipping)`));\n } else {\n const configContent = template.getConfig();\n\n if (typeof configContent === 'string') {\n // Write as plain text (e.g., TOML for wrangler.toml)\n await fsp.writeFile(configPath, configContent, 'utf8');\n } else {\n // Write as JSON\n await writeJSON(configPath, configContent);\n }\n console.log(c('green', ` Generated ${template.configFileName}`));\n }\n }\n}\n\n/**\n * Build the FrontMCP server for a specific deployment target.\n *\n * @param opts - Build options from CLI arguments\n *\n * @example\n * ```bash\n * # Build for Node.js (default)\n * frontmcp build\n *\n * # Build for Vercel\n * frontmcp build --adapter vercel\n *\n * # Build for AWS Lambda\n * frontmcp build --adapter lambda\n *\n * # Build for Cloudflare Workers\n * frontmcp build --adapter cloudflare\n * ```\n */\nexport async function runBuild(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n const adapter = (opts.adapter || 'node') as AdapterName;\n await ensureDir(outDir);\n\n // Validate adapter\n const template = ADAPTERS[adapter];\n if (!template) {\n const available = Object.keys(ADAPTERS).join(', ');\n throw new Error(`Unknown adapter: ${adapter}. Available: ${available}`);\n }\n\n // Warn about experimental adapters\n if (adapter === 'cloudflare') {\n console.log(\n c('yellow', '⚠️ Cloudflare Workers adapter is experimental. See docs for limitations.'),\n );\n }\n\n const moduleFormat = template.moduleFormat;\n\n console.log(`${c('cyan', '[build]')} entry: ${path.relative(cwd, entry)}`);\n console.log(`${c('cyan', '[build]')} outDir: ${path.relative(cwd, outDir)}`);\n console.log(`${c('cyan', '[build]')} adapter: ${adapter} (${moduleFormat})`);\n\n // Build TypeScript compiler arguments\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const args: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n console.log(c('gray', `[build] tsconfig.json detected — compiling with project settings`));\n args.push('--project', tsconfigPath);\n } else {\n args.push(entry);\n args.push('--rootDir', path.dirname(entry));\n if (!isTsLike(entry)) {\n args.push('--allowJs');\n console.log(c('yellow', '[build] Entry is not TypeScript; enabling --allowJs'));\n }\n args.push('--experimentalDecorators', '--emitDecoratorMetadata');\n args.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n // Always pass module format to override tsconfig\n args.push('--module', moduleFormat);\n args.push('--outDir', outDir);\n args.push('--skipLibCheck');\n\n // Run TypeScript compiler\n await runCmd('npx', args);\n\n // Generate adapter-specific files\n if (adapter !== 'node') {\n console.log(c('cyan', `[build] Generating ${adapter} deployment files...`));\n const entryBasename = path.basename(entry);\n await generateAdapterFiles(adapter, outDir, entryBasename, cwd);\n }\n\n console.log(c('green', '✅ Build completed.'));\n console.log(c('gray', `Output placed in ${path.relative(cwd, outDir)}`));\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/build/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { ParsedArgs } from '../../args';\nimport { c } from '../../colors';\nimport { ensureDir, fileExists, fsp, runCmd, resolveEntry, writeJSON } from '../../utils/fs';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../tsconfig';\nimport { ADAPTERS } from './adapters';\nimport { AdapterName } from './types';\nimport { bundleForServerless } from './bundler';\n\nfunction isTsLike(p: string): boolean {\n return /\\.tsx?$/i.test(p);\n}\n\n/**\n * Generate adapter-specific entry point and config files.\n */\nasync function generateAdapterFiles(\n adapter: AdapterName,\n outDir: string,\n entryBasename: string,\n cwd: string,\n): Promise<void> {\n const template = ADAPTERS[adapter];\n\n // Generate serverless setup file first (if adapter has one)\n // This file sets FRONTMCP_SERVERLESS=1 before any imports run\n if (template.getSetupTemplate) {\n const setupContent = template.getSetupTemplate();\n const setupPath = path.join(outDir, 'serverless-setup.js');\n await fsp.writeFile(setupPath, setupContent, 'utf8');\n console.log(c('green', ` Generated serverless setup at ${path.relative(cwd, setupPath)}`));\n }\n\n // Generate index.js entry point\n const mainModuleName = entryBasename.replace(/\\.tsx?$/, '.js');\n const entryContent = template.getEntryTemplate(`./${mainModuleName}`);\n\n // Skip if no entry template (e.g., node adapter)\n if (entryContent) {\n const entryPath = path.join(outDir, 'index.js');\n await fsp.writeFile(entryPath, entryContent, 'utf8');\n console.log(c('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));\n }\n\n // Bundle if adapter requires it (creates single CJS file for serverless)\n if (template.shouldBundle && template.bundleOutput) {\n console.log(c('cyan', `[build] Bundling for ${adapter}...`));\n const entryPath = path.join(outDir, 'index.js');\n await bundleForServerless(entryPath, outDir, template.bundleOutput);\n console.log(c('green', ` Created bundle: ${template.bundleOutput}`));\n }\n\n // Generate config file if adapter has one (skip if already exists)\n if (template.getConfig && template.configFileName) {\n const configPath = path.join(cwd, template.configFileName);\n\n if (await fileExists(configPath)) {\n console.log(c('yellow', ` ${template.configFileName} already exists (skipping)`));\n } else {\n const configContent = template.getConfig();\n\n if (typeof configContent === 'string') {\n // Write as plain text (e.g., TOML for wrangler.toml)\n await fsp.writeFile(configPath, configContent, 'utf8');\n } else {\n // Write as JSON\n await writeJSON(configPath, configContent);\n }\n console.log(c('green', ` Generated ${template.configFileName}`));\n }\n }\n}\n\n/**\n * Build the FrontMCP server for a specific deployment target.\n *\n * @param opts - Build options from CLI arguments\n *\n * @example\n * ```bash\n * # Build for Node.js (default)\n * frontmcp build\n *\n * # Build for Vercel\n * frontmcp build --adapter vercel\n *\n * # Build for AWS Lambda\n * frontmcp build --adapter lambda\n *\n * # Build for Cloudflare Workers\n * frontmcp build --adapter cloudflare\n * ```\n */\nexport async function runBuild(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n const adapter = (opts.adapter || 'node') as AdapterName;\n await ensureDir(outDir);\n\n // Validate adapter\n const template = ADAPTERS[adapter];\n if (!template) {\n const available = Object.keys(ADAPTERS).join(', ');\n throw new Error(`Unknown adapter: ${adapter}. Available: ${available}`);\n }\n\n // Warn about experimental adapters\n if (adapter === 'cloudflare') {\n console.log(\n c('yellow', '⚠️ Cloudflare Workers adapter is experimental. See docs for limitations.'),\n );\n }\n\n const moduleFormat = template.moduleFormat;\n\n console.log(`${c('cyan', '[build]')} entry: ${path.relative(cwd, entry)}`);\n console.log(`${c('cyan', '[build]')} outDir: ${path.relative(cwd, outDir)}`);\n console.log(`${c('cyan', '[build]')} adapter: ${adapter} (${moduleFormat})`);\n\n // Build TypeScript compiler arguments\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const args: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n console.log(c('gray', `[build] tsconfig.json detected — compiling with project settings`));\n args.push('--project', tsconfigPath);\n } else {\n args.push(entry);\n args.push('--rootDir', path.dirname(entry));\n if (!isTsLike(entry)) {\n args.push('--allowJs');\n console.log(c('yellow', '[build] Entry is not TypeScript; enabling --allowJs'));\n }\n args.push('--experimentalDecorators', '--emitDecoratorMetadata');\n args.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n // Always pass module format to override tsconfig\n args.push('--module', moduleFormat);\n args.push('--outDir', outDir);\n args.push('--skipLibCheck');\n\n // Run TypeScript compiler\n await runCmd('npx', args);\n\n // Generate adapter-specific files\n if (adapter !== 'node') {\n console.log(c('cyan', `[build] Generating ${adapter} deployment files...`));\n const entryBasename = path.basename(entry);\n await generateAdapterFiles(adapter, outDir, entryBasename, cwd);\n }\n\n console.log(c('green', '✅ Build completed.'));\n console.log(c('gray', `Output placed in ${path.relative(cwd, outDir)}`));\n}\n"]}
@@ -12,6 +12,22 @@ export type AdapterTemplate = {
12
12
  * @returns The content for index.js, or empty string if no wrapper needed
13
13
  */
14
14
  getEntryTemplate: (mainModulePath: string) => string;
15
+ /**
16
+ * Generate the serverless setup file content.
17
+ * This file is imported first to set environment variables before decorators run.
18
+ * @returns The content for serverless-setup.js, or undefined if not needed
19
+ */
20
+ getSetupTemplate?: () => string;
21
+ /**
22
+ * Whether to bundle the output with rspack.
23
+ * Recommended for serverless deployments to avoid ESM/CJS issues.
24
+ */
25
+ shouldBundle?: boolean;
26
+ /**
27
+ * Output filename for the bundled file (e.g., 'handler.cjs').
28
+ * Only used when shouldBundle is true.
29
+ */
30
+ bundleOutput?: string;
15
31
  /**
16
32
  * Generate the deployment platform config file content.
17
33
  * @returns Object (for JSON) or string (for TOML/YAML)
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/build/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a deployment adapter.\n * Each adapter defines how to compile and package the FrontMCP server\n * for a specific deployment target.\n */\nexport type AdapterTemplate = {\n /** Module format for TypeScript compilation */\n moduleFormat: 'commonjs' | 'esnext';\n\n /**\n * Generate the entry point file content.\n * @param mainModulePath - Relative path to the compiled main module (e.g., './main.js')\n * @returns The content for index.js, or empty string if no wrapper needed\n */\n getEntryTemplate: (mainModulePath: string) => string;\n\n /**\n * Generate the deployment platform config file content.\n * @returns Object (for JSON) or string (for TOML/YAML)\n */\n getConfig?: () => object | string;\n\n /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */\n configFileName?: string;\n};\n\nexport type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare';\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/build/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a deployment adapter.\n * Each adapter defines how to compile and package the FrontMCP server\n * for a specific deployment target.\n */\nexport type AdapterTemplate = {\n /** Module format for TypeScript compilation */\n moduleFormat: 'commonjs' | 'esnext';\n\n /**\n * Generate the entry point file content.\n * @param mainModulePath - Relative path to the compiled main module (e.g., './main.js')\n * @returns The content for index.js, or empty string if no wrapper needed\n */\n getEntryTemplate: (mainModulePath: string) => string;\n\n /**\n * Generate the serverless setup file content.\n * This file is imported first to set environment variables before decorators run.\n * @returns The content for serverless-setup.js, or undefined if not needed\n */\n getSetupTemplate?: () => string;\n\n /**\n * Whether to bundle the output with rspack.\n * Recommended for serverless deployments to avoid ESM/CJS issues.\n */\n shouldBundle?: boolean;\n\n /**\n * Output filename for the bundled file (e.g., 'handler.cjs').\n * Only used when shouldBundle is true.\n */\n bundleOutput?: string;\n\n /**\n * Generate the deployment platform config file content.\n * @returns Object (for JSON) or string (for TOML/YAML)\n */\n getConfig?: () => object | string;\n\n /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */\n configFileName?: string;\n};\n\nexport type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare';\n"]}