frontmcp 0.6.1 → 0.6.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -71,7 +71,7 @@ module.exports = {
71
71
  }
72
72
  };
73
73
  `,
74
- getConfig: () => `name = "frontmcp-worker"
74
+ getConfig: (_cwd) => `name = "frontmcp-worker"
75
75
  main = "dist/index.js"
76
76
  compatibility_date = "2024-01-01"
77
77
  `,
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;WAIrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,GAAG,EAAE,CAAC;;;CAGlB;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --adapter cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n getConfig: () => `name = \"frontmcp-worker\"\nmain = \"dist/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;WAIrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --adapter cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
@@ -3,12 +3,17 @@ import { AdapterTemplate } from '../types';
3
3
  * Vercel adapter - serverless deployment on Vercel.
4
4
  * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
5
5
  *
6
+ * Uses Vercel Build Output API for deployment:
7
+ * - Creates .vercel/output/config.json with routing
8
+ * - Creates .vercel/output/functions/index.func/ with handler
9
+ *
6
10
  * The build process:
7
11
  * 1. TypeScript compiles to ESM in dist/
8
12
  * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
9
13
  * 3. index.js imports setup first, then main module
10
14
  * 4. rspack bundles everything into handler.cjs
15
+ * 5. Build Output API structure is created in .vercel/output/
11
16
  *
12
- * @see https://vercel.com/docs/frameworks/express
17
+ * @see https://vercel.com/docs/build-output-api/v3
13
18
  */
14
19
  export declare const vercelAdapter: AdapterTemplate;
@@ -1,17 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.vercelAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const fs = tslib_1.__importStar(require("fs/promises"));
7
+ const fs_1 = require("fs");
8
+ const PACKAGE_MANAGERS = {
9
+ bun: { install: 'bun install', run: 'bun run build' },
10
+ pnpm: { install: 'pnpm install', run: 'pnpm run build' },
11
+ yarn: { install: 'yarn install', run: 'yarn build' },
12
+ npm: { install: 'npm install', run: 'npm run build' },
13
+ };
14
+ const LOCKFILE_TO_PM = {
15
+ 'bun.lockb': 'bun',
16
+ 'pnpm-lock.yaml': 'pnpm',
17
+ 'yarn.lock': 'yarn',
18
+ 'package-lock.json': 'npm',
19
+ };
20
+ /**
21
+ * Detect package manager based on lockfile presence.
22
+ * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)
23
+ */
24
+ function detectPackageManager(cwd) {
25
+ for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {
26
+ if ((0, fs_1.existsSync)(path.join(cwd, lockfile))) {
27
+ return pm;
28
+ }
29
+ }
30
+ return 'npm'; // Default fallback
31
+ }
4
32
  /**
5
33
  * Vercel adapter - serverless deployment on Vercel.
6
34
  * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
7
35
  *
36
+ * Uses Vercel Build Output API for deployment:
37
+ * - Creates .vercel/output/config.json with routing
38
+ * - Creates .vercel/output/functions/index.func/ with handler
39
+ *
8
40
  * The build process:
9
41
  * 1. TypeScript compiles to ESM in dist/
10
42
  * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)
11
43
  * 3. index.js imports setup first, then main module
12
44
  * 4. rspack bundles everything into handler.cjs
45
+ * 5. Build Output API structure is created in .vercel/output/
13
46
  *
14
- * @see https://vercel.com/docs/frameworks/express
47
+ * @see https://vercel.com/docs/build-output-api/v3
15
48
  */
16
49
  exports.vercelAdapter = {
17
50
  moduleFormat: 'esnext',
@@ -38,11 +71,63 @@ export default async function handler(req, res) {
38
71
  return app(req, res);
39
72
  }
40
73
  `,
41
- getConfig: () => ({
42
- version: 2,
43
- builds: [{ src: 'dist/handler.cjs', use: '@vercel/node' }],
44
- routes: [{ src: '/(.*)', dest: '/dist/handler.cjs' }],
45
- }),
74
+ // Detect package manager and generate appropriate vercel.json
75
+ getConfig: (cwd) => {
76
+ const pm = detectPackageManager(cwd);
77
+ const config = PACKAGE_MANAGERS[pm];
78
+ return {
79
+ version: 2,
80
+ buildCommand: config.run,
81
+ installCommand: config.install,
82
+ };
83
+ },
46
84
  configFileName: 'vercel.json',
85
+ /**
86
+ * Create Vercel Build Output API structure after bundling.
87
+ * This allows Vercel to deploy the function without needing an /api folder.
88
+ *
89
+ * Structure created:
90
+ * .vercel/output/
91
+ * ├── config.json (routes all requests to index function)
92
+ * └── functions/
93
+ * └── index.func/
94
+ * ├── .vc-config.json (Node.js 22 runtime config)
95
+ * └── handler.cjs (bundled handler + chunks)
96
+ */
97
+ postBundle: async (outDir, cwd, bundleOutput) => {
98
+ const outputDir = path.join(cwd, '.vercel', 'output');
99
+ const funcDir = path.join(outputDir, 'functions', 'index.func');
100
+ // Create directories
101
+ await fs.mkdir(funcDir, { recursive: true });
102
+ // Copy all files from dist to the function directory
103
+ // This includes handler.cjs and any chunk files (*.handler.cjs)
104
+ const distFiles = await fs.readdir(outDir);
105
+ for (const file of distFiles) {
106
+ const srcPath = path.join(outDir, file);
107
+ const destPath = path.join(funcDir, file);
108
+ const stat = await fs.stat(srcPath);
109
+ if (stat.isDirectory()) {
110
+ // Recursively copy directories
111
+ await fs.cp(srcPath, destPath, { recursive: true });
112
+ }
113
+ else {
114
+ // Copy files
115
+ await fs.copyFile(srcPath, destPath);
116
+ }
117
+ }
118
+ // Create function config (.vc-config.json)
119
+ const vcConfig = {
120
+ runtime: 'nodejs22.x',
121
+ handler: bundleOutput,
122
+ launcherType: 'Nodejs',
123
+ };
124
+ await fs.writeFile(path.join(funcDir, '.vc-config.json'), JSON.stringify(vcConfig, null, 2));
125
+ // Create output config (config.json) with routing
126
+ const outputConfig = {
127
+ version: 3,
128
+ routes: [{ src: '/(.*)', dest: '/index' }],
129
+ };
130
+ await fs.writeFile(path.join(outputDir, 'config.json'), JSON.stringify(outputConfig, null, 2));
131
+ },
47
132
  };
48
133
  //# sourceMappingURL=vercel.js.map
@@ -1 +1 @@
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"]}
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport * as fs from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\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 * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\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 // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * └── handler.cjs (bundled handler + chunks)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await fs.mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await fs.readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const stat = await fs.stat(srcPath);\n\n if (stat.isDirectory()) {\n // Recursively copy directories\n await fs.cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await fs.copyFile(srcPath, destPath);\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs22.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await fs.writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await fs.writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
@@ -29,14 +29,28 @@ async function bundleForServerless(entryPath, outDir, outputFilename) {
29
29
  '@swc/core': '@swc/core',
30
30
  fsevents: 'fsevents',
31
31
  esbuild: 'esbuild',
32
+ // React is optional - only needed for MDX/JSX rendering
33
+ react: 'react',
34
+ 'react-dom': 'react-dom',
35
+ 'react-dom/server': 'react-dom/server',
36
+ 'react/jsx-runtime': 'react/jsx-runtime',
32
37
  },
33
38
  resolve: {
34
39
  extensions: ['.js', '.mjs', '.cjs', '.json'],
40
+ // Allow imports without file extensions (TypeScript compiles without .js but ESM requires them)
41
+ fullySpecified: false,
35
42
  },
36
43
  // Don't minimize to preserve readability for debugging
37
44
  optimization: {
38
45
  minimize: false,
39
46
  },
47
+ // Suppress known third-party library warnings that don't affect runtime
48
+ ignoreWarnings: [
49
+ // Express view engine dynamic require - expected behavior, harmless at runtime
50
+ /Critical dependency: the request of a dependency is an expression/,
51
+ // Handlebars require.extensions - deprecated Node.js API but works at runtime
52
+ /require\.extensions is not supported by Rspack/,
53
+ ],
40
54
  // Suppress verbose output
41
55
  stats: 'errors-warnings',
42
56
  });
@@ -1 +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"]}
1
+ {"version":3,"file":"bundler.js","sourceRoot":"","sources":["../../../../src/commands/build/bundler.ts"],"names":[],"mappings":";;AAWA,kDAwEC;AAnFD,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;YAClB,wDAAwD;YACxD,KAAK,EAAE,OAAO;YACd,WAAW,EAAE,WAAW;YACxB,kBAAkB,EAAE,kBAAkB;YACtC,mBAAmB,EAAE,mBAAmB;SACzC;QACD,OAAO,EAAE;YACP,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;YAC5C,gGAAgG;YAChG,cAAc,EAAE,KAAK;SACtB;QACD,uDAAuD;QACvD,YAAY,EAAE;YACZ,QAAQ,EAAE,KAAK;SAChB;QACD,wEAAwE;QACxE,cAAc,EAAE;YACd,+EAA+E;YAC/E,mEAAmE;YACnE,8EAA8E;YAC9E,gDAAgD;SACjD;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 // React is optional - only needed for MDX/JSX rendering\n react: 'react',\n 'react-dom': 'react-dom',\n 'react-dom/server': 'react-dom/server',\n 'react/jsx-runtime': 'react/jsx-runtime',\n },\n resolve: {\n extensions: ['.js', '.mjs', '.cjs', '.json'],\n // Allow imports without file extensions (TypeScript compiles without .js but ESM requires them)\n fullySpecified: false,\n },\n // Don't minimize to preserve readability for debugging\n optimization: {\n minimize: false,\n },\n // Suppress known third-party library warnings that don't affect runtime\n ignoreWarnings: [\n // Express view engine dynamic require - expected behavior, harmless at runtime\n /Critical dependency: the request of a dependency is an expression/,\n // Handlebars require.extensions - deprecated Node.js API but works at runtime\n /require\\.extensions is not supported by Rspack/,\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"]}
@@ -39,6 +39,12 @@ async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
39
39
  const entryPath = path.join(outDir, 'index.js');
40
40
  await (0, bundler_1.bundleForServerless)(entryPath, outDir, template.bundleOutput);
41
41
  console.log((0, colors_1.c)('green', ` Created bundle: ${template.bundleOutput}`));
42
+ // Run post-bundle hook if defined (e.g., create Build Output API structure)
43
+ if (template.postBundle) {
44
+ console.log((0, colors_1.c)('cyan', `[build] Creating ${adapter} deployment structure...`));
45
+ await template.postBundle(outDir, cwd, template.bundleOutput);
46
+ console.log((0, colors_1.c)('green', ` Created deployment output structure`));
47
+ }
42
48
  }
43
49
  // Generate config file if adapter has one (skip if already exists)
44
50
  if (template.getConfig && template.configFileName) {
@@ -47,7 +53,7 @@ async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
47
53
  console.log((0, colors_1.c)('yellow', ` ${template.configFileName} already exists (skipping)`));
48
54
  }
49
55
  else {
50
- const configContent = template.getConfig();
56
+ const configContent = template.getConfig(cwd);
51
57
  if (typeof configContent === 'string') {
52
58
  // Write as plain text (e.g., TOML for wrangler.toml)
53
59
  await fs_1.fsp.writeFile(configPath, configContent, 'utf8');
@@ -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';\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"]}
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 // Run post-bundle hook if defined (e.g., create Build Output API structure)\n if (template.postBundle) {\n console.log(c('cyan', `[build] Creating ${adapter} deployment structure...`));\n await template.postBundle(outDir, cwd, template.bundleOutput);\n console.log(c('green', ` Created deployment output structure`));\n }\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(cwd);\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"]}
@@ -30,10 +30,19 @@ export type AdapterTemplate = {
30
30
  bundleOutput?: string;
31
31
  /**
32
32
  * Generate the deployment platform config file content.
33
+ * @param cwd - Current working directory (for detecting package manager, etc.)
33
34
  * @returns Object (for JSON) or string (for TOML/YAML)
34
35
  */
35
- getConfig?: () => object | string;
36
+ getConfig?: (cwd: string) => object | string;
36
37
  /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */
37
38
  configFileName?: string;
39
+ /**
40
+ * Post-bundle hook for creating deployment-specific output structure.
41
+ * Called after bundling is complete.
42
+ * @param outDir - The output directory (e.g., 'dist')
43
+ * @param cwd - Current working directory
44
+ * @param bundleOutput - Name of the bundled file (e.g., 'handler.cjs')
45
+ */
46
+ postBundle?: (outDir: string, cwd: string, bundleOutput: string) => Promise<void>;
38
47
  };
39
48
  export type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare';
@@ -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 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"]}
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 * @param cwd - Current working directory (for detecting package manager, etc.)\n * @returns Object (for JSON) or string (for TOML/YAML)\n */\n getConfig?: (cwd: string) => object | string;\n\n /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */\n configFileName?: string;\n\n /**\n * Post-bundle hook for creating deployment-specific output structure.\n * Called after bundling is complete.\n * @param outDir - The output directory (e.g., 'dist')\n * @param cwd - Current working directory\n * @param bundleOutput - Name of the bundled file (e.g., 'handler.cjs')\n */\n postBundle?: (outDir: string, cwd: string, bundleOutput: string) => Promise<void>;\n};\n\nexport type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare';\n"]}