@frontmcp/nx 1.8.7 → 1.9.0

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 (50) hide show
  1. package/generators/app/files/project.json__tmpl__ +9 -0
  2. package/generators/app/files/src/tools/hello.tool.spec.ts__tmpl__ +10 -0
  3. package/generators/app/files/tsconfig.json__tmpl__ +8 -3
  4. package/generators/app/lib/normalize-options.d.ts +3 -1
  5. package/generators/app/lib/normalize-options.js +1 -1
  6. package/generators/app/lib/normalize-options.js.map +1 -1
  7. package/generators/lib/files/adapter/src/__fileName__.adapter.spec.ts__tmpl__ +9 -0
  8. package/generators/lib/files/generic/src/__fileName__.spec.ts__tmpl__ +7 -0
  9. package/generators/lib/files/plugin/src/__fileName__.plugin.spec.ts__tmpl__ +11 -0
  10. package/generators/lib/files/tool-register/src/__fileName__.tools.spec.ts__tmpl__ +14 -0
  11. package/generators/lib/lib/normalize-options.d.ts +3 -1
  12. package/generators/lib/lib/normalize-options.js +1 -1
  13. package/generators/lib/lib/normalize-options.js.map +1 -1
  14. package/generators/lib/lib-project-files/project.json__tmpl__ +20 -4
  15. package/generators/lib/lib-project-files/tsconfig.json__tmpl__ +8 -1
  16. package/generators/lib/lib-project-files/tsconfig.spec.json__tmpl__ +0 -3
  17. package/generators/lib/lib.d.ts +1 -1
  18. package/generators/lib/lib.js +51 -19
  19. package/generators/lib/lib.js.map +1 -1
  20. package/generators/server/files/common/project.json__tmpl__ +15 -1
  21. package/generators/server/files/common/tsconfig.json__tmpl__ +8 -3
  22. package/generators/server/files/lambda/template.yaml__tmpl__ +4 -2
  23. package/generators/server/files/node/Dockerfile.dockerignore__tmpl__ +10 -0
  24. package/generators/server/files/node/Dockerfile__tmpl__ +6 -1
  25. package/generators/server/files/node/docker-compose.yml__tmpl__ +1 -1
  26. package/generators/server/files/vercel/vercel.json__tmpl__ +3 -12
  27. package/generators/server/lib/normalize-options.d.ts +8 -1
  28. package/generators/server/lib/normalize-options.js +8 -2
  29. package/generators/server/lib/normalize-options.js.map +1 -1
  30. package/generators/server/server.js +7 -0
  31. package/generators/server/server.js.map +1 -1
  32. package/generators/ui-shared/add-ui-entry.js +2 -12
  33. package/generators/ui-shared/add-ui-entry.js.map +1 -1
  34. package/generators/ui-shared/ensure-ui-package.js +8 -5
  35. package/generators/ui-shared/ensure-ui-package.js.map +1 -1
  36. package/generators/workspace/files/__dot__gitignore__tmpl__ +1 -0
  37. package/generators/workspace/files/nx.json__tmpl__ +0 -10
  38. package/package.json +2 -2
  39. package/utils/package-manager.d.ts +11 -0
  40. package/utils/package-manager.js +33 -0
  41. package/utils/package-manager.js.map +1 -0
  42. package/utils/project-paths.d.ts +24 -4
  43. package/utils/project-paths.js +55 -8
  44. package/utils/project-paths.js.map +1 -1
  45. package/utils/versions.d.ts +8 -0
  46. package/utils/versions.js +28 -0
  47. package/utils/versions.js.map +1 -1
  48. package/generators/lib/files/adapter/src/__fileName__.adapter.ts__tmpl__ +0 -19
  49. package/generators/lib/files/plugin/src/__fileName__.plugin.ts__tmpl__ +0 -19
  50. package/generators/server/files/node/__dot__dockerignore__tmpl__ +0 -8
@@ -31,6 +31,15 @@
31
31
  "cache": true,
32
32
  "options": {}
33
33
  },
34
+ "typecheck": {
35
+ "executor": "nx:run-commands",
36
+ "cache": true,
37
+ "options": {
38
+ "commands": ["tsc --noEmit -p tsconfig.lib.json", "tsc --noEmit -p tsconfig.spec.json"],
39
+ "parallel": false,
40
+ "cwd": "{projectRoot}"
41
+ }
42
+ },
34
43
  "inspector": {
35
44
  "executor": "@frontmcp/nx:inspector",
36
45
  "options": {}
@@ -0,0 +1,10 @@
1
+ import HelloTool from './hello.tool';
2
+
3
+ describe('HelloTool', () => {
4
+ it('greets the caller by name', async () => {
5
+ // execute() holds the tool's logic; build the tool without the request context the server injects.
6
+ const tool: HelloTool = Object.create(HelloTool.prototype);
7
+
8
+ await expect(tool.execute({ name: 'Ada' })).resolves.toEqual({ message: 'Hello, Ada!' });
9
+ });
10
+ });
@@ -2,13 +2,18 @@
2
2
  "extends": "<%= offset %>tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "module": "commonjs",
5
- "moduleResolution": "node10",<% if (ignoreDeprecations) { %>
6
- "ignoreDeprecations": "<%= ignoreDeprecations %>",<% } %>
5
+ "moduleResolution": "<%= moduleResolution %>",<% if (resetCustomConditions) { %>
6
+ "customConditions": null,<% } %>
7
+ "rootDir": "<%= offset %>",
8
+ "composite": false,
9
+ "declarationMap": false,
10
+ "emitDeclarationOnly": false,
7
11
  "forceConsistentCasingInFileNames": true,
8
12
  "strict": true,
9
13
  "emitDecoratorMetadata": true,
10
14
  "experimentalDecorators": true
11
15
  },
12
16
  "include": ["src/**/*.ts"],
13
- "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
17
+ "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"],
18
+ "nx": { "addTypecheckTarget": false }
14
19
  }
@@ -1,5 +1,6 @@
1
1
  import { type Tree } from '@nx/devkit';
2
2
  import type { AppGeneratorSchema } from '../schema.js';
3
+ import { type ProjectModuleResolution } from '../../../utils/project-paths.js';
3
4
  export interface NormalizedAppOptions {
4
5
  name: string;
5
6
  projectName: string;
@@ -9,7 +10,8 @@ export interface NormalizedAppOptions {
9
10
  outputRoot: string;
10
11
  /** Path from the project back to the workspace root, e.g. `../../`. */
11
12
  offset: string;
12
- ignoreDeprecations: string;
13
+ moduleResolution: ProjectModuleResolution;
14
+ resetCustomConditions: boolean;
13
15
  className: string;
14
16
  fileName: string;
15
17
  propertyName: string;
@@ -14,7 +14,7 @@ function normalizeOptions(tree, schema) {
14
14
  projectRoot,
15
15
  outputRoot,
16
16
  offset,
17
- ignoreDeprecations: (0, project_paths_js_1.getIgnoreDeprecations)(tree, schema.workspaceRoot),
17
+ ...(0, project_paths_js_1.getProjectTsOptions)(tree, schema.workspaceRoot),
18
18
  className,
19
19
  fileName,
20
20
  propertyName,
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/app/lib/normalize-options.ts"],"names":[],"mappings":";;AAqBA,4CAmBC;AAxCD,uCAAiE;AAEjE,sEAA6F;AAmB7F,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA0B;IACrE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,MAAM,CAAC,aAAa,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvG,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,UAAU,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAE9F,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,QAAQ;QACrB,WAAW;QACX,UAAU;QACV,MAAM;QACN,kBAAkB,EAAE,IAAA,wCAAqB,EAAC,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC;QACrE,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, names, joinPathFragments } from '@nx/devkit';\nimport type { AppGeneratorSchema } from '../schema.js';\nimport { getIgnoreDeprecations, resolveProjectPaths } from '../../../utils/project-paths.js';\n\nexport interface NormalizedAppOptions {\n name: string;\n projectName: string;\n /** Project root relative to the Nx workspace root. */\n projectRoot: string;\n /** Where the files are written, relative to the tree root. */\n outputRoot: string;\n /** Path from the project back to the workspace root, e.g. `../../`. */\n offset: string;\n ignoreDeprecations: string;\n className: string;\n fileName: string;\n propertyName: string;\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: AppGeneratorSchema): NormalizedAppOptions {\n const { className, fileName, propertyName } = names(schema.name);\n const outputRoot = schema.directory ?? joinPathFragments(schema.workspaceRoot ?? '', 'apps', fileName);\n const { projectRoot, offset } = resolveProjectPaths(outputRoot, schema.workspaceRoot);\n const parsedTags = schema.tags ? schema.tags.split(',').map((t) => t.trim()) : ['scope:apps'];\n\n return {\n name: schema.name,\n projectName: fileName,\n projectRoot,\n outputRoot,\n offset,\n ignoreDeprecations: getIgnoreDeprecations(tree, schema.workspaceRoot),\n className,\n fileName,\n propertyName,\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
1
+ {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/app/lib/normalize-options.ts"],"names":[],"mappings":";;AAsBA,4CAmBC;AAzCD,uCAAiE;AAEjE,sEAAyH;AAoBzH,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA0B;IACrE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,MAAM,CAAC,aAAa,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvG,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,UAAU,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAE9F,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,QAAQ;QACrB,WAAW;QACX,UAAU;QACV,MAAM;QACN,GAAG,IAAA,sCAAmB,EAAC,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC;QAClD,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, names, joinPathFragments } from '@nx/devkit';\nimport type { AppGeneratorSchema } from '../schema.js';\nimport { getProjectTsOptions, resolveProjectPaths, type ProjectModuleResolution } from '../../../utils/project-paths.js';\n\nexport interface NormalizedAppOptions {\n name: string;\n projectName: string;\n /** Project root relative to the Nx workspace root. */\n projectRoot: string;\n /** Where the files are written, relative to the tree root. */\n outputRoot: string;\n /** Path from the project back to the workspace root, e.g. `../../`. */\n offset: string;\n moduleResolution: ProjectModuleResolution;\n resetCustomConditions: boolean;\n className: string;\n fileName: string;\n propertyName: string;\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: AppGeneratorSchema): NormalizedAppOptions {\n const { className, fileName, propertyName } = names(schema.name);\n const outputRoot = schema.directory ?? joinPathFragments(schema.workspaceRoot ?? '', 'apps', fileName);\n const { projectRoot, offset } = resolveProjectPaths(outputRoot, schema.workspaceRoot);\n const parsedTags = schema.tags ? schema.tags.split(',').map((t) => t.trim()) : ['scope:apps'];\n\n return {\n name: schema.name,\n projectName: fileName,\n projectRoot,\n outputRoot,\n offset,\n ...getProjectTsOptions(tree, schema.workspaceRoot),\n className,\n fileName,\n propertyName,\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { <%= className %>Adapter } from './<%= fileName %>.adapter';
2
+
3
+ describe('<%= className %>Adapter', () => {
4
+ it('returns no tools, resources or prompts yet', async () => {
5
+ const adapter = new <%= className %>Adapter({ name: '<%= fileName %>' });
6
+
7
+ await expect(adapter.fetch()).resolves.toEqual({ tools: [], resources: [], prompts: [] });
8
+ });
9
+ });
@@ -0,0 +1,7 @@
1
+ import { <%= className %> } from './<%= fileName %>';
2
+
3
+ describe('<%= className %>', () => {
4
+ it('greets by name', () => {
5
+ expect(new <%= className %>().greet('Ada')).toBe('Hello from <%= className %>, Ada!');
6
+ });
7
+ });
@@ -0,0 +1,11 @@
1
+ import { <%= className %>Plugin } from './<%= fileName %>.plugin';
2
+
3
+ describe('<%= className %>Plugin', () => {
4
+ it('keeps the options it is created with', () => {
5
+ expect(new <%= className %>Plugin({}).options).toEqual({});
6
+ });
7
+
8
+ it('contributes no providers yet', () => {
9
+ expect(<%= className %>Plugin.dynamicProviders({})).toEqual([]);
10
+ });
11
+ });
@@ -0,0 +1,14 @@
1
+ import { ExampleTool, <%= className %>Tools } from './<%= fileName %>.tools';
2
+
3
+ describe('<%= className %>Tools', () => {
4
+ it('registers the example tool', () => {
5
+ expect(<%= className %>Tools).toContain(ExampleTool);
6
+ });
7
+
8
+ it('echoes the value', async () => {
9
+ // execute() holds the tool's logic; build the tool without the request context the server injects.
10
+ const tool: ExampleTool = Object.create(ExampleTool.prototype);
11
+
12
+ await expect(tool.execute({ value: 'ping' })).resolves.toEqual({ result: 'ping' });
13
+ });
14
+ });
@@ -1,11 +1,13 @@
1
1
  import { type Tree } from '@nx/devkit';
2
2
  import type { LibGeneratorSchema } from '../schema.js';
3
+ import { type ProjectModuleResolution } from '../../../utils/project-paths.js';
3
4
  export interface NormalizedLibOptions {
4
5
  name: string;
5
6
  projectName: string;
6
7
  projectRoot: string;
7
8
  offset: string;
8
- ignoreDeprecations: string;
9
+ moduleResolution: ProjectModuleResolution;
10
+ resetCustomConditions: boolean;
9
11
  className: string;
10
12
  fileName: string;
11
13
  propertyName: string;
@@ -20,7 +20,7 @@ function normalizeOptions(tree, schema) {
20
20
  projectName: fileName,
21
21
  projectRoot,
22
22
  offset,
23
- ignoreDeprecations: (0, project_paths_js_1.getIgnoreDeprecations)(tree),
23
+ ...(0, project_paths_js_1.getProjectTsOptions)(tree),
24
24
  className,
25
25
  fileName,
26
26
  propertyName,
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/lib/lib/normalize-options.ts"],"names":[],"mappings":";;AAoBA,4CA6BC;AAjDD,uCAAiE;AAEjE,sEAA6F;AAkB7F,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA0B;IACrE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7G,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,SAAS,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,aAAa,QAAQ,EAAE,CAAC;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAEnB,IAAI,WAAW,EAAE,CAAC;QAChB,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,QAAQ;QACrB,WAAW;QACX,MAAM;QACN,kBAAkB,EAAE,IAAA,wCAAqB,EAAC,IAAI,CAAC;QAC/C,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,OAAO;QACP,WAAW;QACX,UAAU;QACV,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, names, joinPathFragments } from '@nx/devkit';\nimport type { LibGeneratorSchema } from '../schema.js';\nimport { getIgnoreDeprecations, resolveProjectPaths } from '../../../utils/project-paths.js';\n\nexport interface NormalizedLibOptions {\n name: string;\n projectName: string;\n projectRoot: string;\n offset: string;\n ignoreDeprecations: string;\n className: string;\n fileName: string;\n propertyName: string;\n libType: 'generic' | 'plugin' | 'adapter' | 'tool-register';\n publishable: boolean;\n importPath: string;\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: LibGeneratorSchema): NormalizedLibOptions {\n const { className, fileName, propertyName } = names(schema.name);\n const { projectRoot, offset } = resolveProjectPaths(schema.directory ?? joinPathFragments('libs', fileName));\n const libType = schema.libType ?? 'generic';\n const publishable = schema.publishable ?? false;\n const importPath = schema.importPath ?? `@frontmcp/${fileName}`;\n const parsedTags = schema.tags\n ? schema.tags.split(',').map((t) => t.trim())\n : ['scope:libs'];\n\n if (publishable) {\n parsedTags.push('scope:publishable');\n }\n\n return {\n name: schema.name,\n projectName: fileName,\n projectRoot,\n offset,\n ignoreDeprecations: getIgnoreDeprecations(tree),\n className,\n fileName,\n propertyName,\n libType,\n publishable,\n importPath,\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
1
+ {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/lib/lib/normalize-options.ts"],"names":[],"mappings":";;AAqBA,4CA6BC;AAlDD,uCAAiE;AAEjE,sEAAyH;AAmBzH,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA0B;IACrE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7G,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,SAAS,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,aAAa,QAAQ,EAAE,CAAC;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAEnB,IAAI,WAAW,EAAE,CAAC;QAChB,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,QAAQ;QACrB,WAAW;QACX,MAAM;QACN,GAAG,IAAA,sCAAmB,EAAC,IAAI,CAAC;QAC5B,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,OAAO;QACP,WAAW;QACX,UAAU;QACV,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, names, joinPathFragments } from '@nx/devkit';\nimport type { LibGeneratorSchema } from '../schema.js';\nimport { getProjectTsOptions, resolveProjectPaths, type ProjectModuleResolution } from '../../../utils/project-paths.js';\n\nexport interface NormalizedLibOptions {\n name: string;\n projectName: string;\n projectRoot: string;\n offset: string;\n moduleResolution: ProjectModuleResolution;\n resetCustomConditions: boolean;\n className: string;\n fileName: string;\n propertyName: string;\n libType: 'generic' | 'plugin' | 'adapter' | 'tool-register';\n publishable: boolean;\n importPath: string;\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: LibGeneratorSchema): NormalizedLibOptions {\n const { className, fileName, propertyName } = names(schema.name);\n const { projectRoot, offset } = resolveProjectPaths(schema.directory ?? joinPathFragments('libs', fileName));\n const libType = schema.libType ?? 'generic';\n const publishable = schema.publishable ?? false;\n const importPath = schema.importPath ?? `@frontmcp/${fileName}`;\n const parsedTags = schema.tags\n ? schema.tags.split(',').map((t) => t.trim())\n : ['scope:libs'];\n\n if (publishable) {\n parsedTags.push('scope:publishable');\n }\n\n return {\n name: schema.name,\n projectName: fileName,\n projectRoot,\n offset,\n ...getProjectTsOptions(tree),\n className,\n fileName,\n propertyName,\n libType,\n publishable,\n importPath,\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
@@ -4,12 +4,28 @@
4
4
  "sourceRoot": "<%= projectRoot %>/src",
5
5
  "projectType": "library",
6
6
  "tags": [<%- parsedTags.map(t => `"${t}"`).join(', ') %>],
7
- "targets": {
7
+ "targets": {<% if (publishable) { %>
8
+ "build": {
9
+ "executor": "nx:run-commands",
10
+ "cache": true,
11
+ "outputs": ["{projectRoot}/dist"],
12
+ "options": {
13
+ "command": "tsc -p tsconfig.lib.json",
14
+ "cwd": "{projectRoot}"
15
+ }
16
+ },<% } %>
8
17
  "test": {
9
- "executor": "@nx/jest:jest",
10
- "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
18
+ "executor": "@frontmcp/nx:test",
19
+ "cache": true,
20
+ "options": {}
21
+ },
22
+ "typecheck": {
23
+ "executor": "nx:run-commands",
24
+ "cache": true,
11
25
  "options": {
12
- "jestConfig": "<%= projectRoot %>/jest.config.cjs"
26
+ "commands": ["tsc --noEmit -p tsconfig.lib.json", "tsc --noEmit -p tsconfig.spec.json"],
27
+ "parallel": false,
28
+ "cwd": "{projectRoot}"
13
29
  }
14
30
  }
15
31
  }
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "extends": "<%= offset %>tsconfig.base.json",
3
3
  "compilerOptions": {
4
+ "module": "commonjs",
5
+ "moduleResolution": "<%= moduleResolution %>",<% if (resetCustomConditions) { %>
6
+ "customConditions": null,<% } %>
7
+ "composite": false,
8
+ "declarationMap": false,
9
+ "emitDeclarationOnly": false,
4
10
  "forceConsistentCasingInFileNames": true,
5
11
  "strict": true,
6
12
  "emitDecoratorMetadata": true,
@@ -11,5 +17,6 @@
11
17
  "references": [
12
18
  { "path": "./tsconfig.lib.json" },
13
19
  { "path": "./tsconfig.spec.json" }
14
- ]
20
+ ],
21
+ "nx": { "addTypecheckTarget": false }
15
22
  }
@@ -2,9 +2,6 @@
2
2
  "extends": "./tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "<%= offset %>dist/out-tsc",
5
- "module": "commonjs",
6
- "moduleResolution": "node10",<% if (ignoreDeprecations) { %>
7
- "ignoreDeprecations": "<%= ignoreDeprecations %>",<% } %>
8
5
  "types": ["jest", "node"]
9
6
  },
10
7
  "include": ["src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
@@ -1,4 +1,4 @@
1
- import { type Tree, type GeneratorCallback } from '@nx/devkit';
1
+ import { type GeneratorCallback, type Tree } from '@nx/devkit';
2
2
  import type { LibGeneratorSchema } from './schema.js';
3
3
  export declare function libGenerator(tree: Tree, schema: LibGeneratorSchema): Promise<GeneratorCallback | void>;
4
4
  export default libGenerator;
@@ -1,34 +1,66 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.libGenerator = libGenerator;
4
- const devkit_1 = require("@nx/devkit");
5
4
  const path_1 = require("path");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const project_paths_js_1 = require("../../utils/project-paths.js");
7
+ const versions_js_1 = require("../../utils/versions.js");
6
8
  const index_js_1 = require("./lib/index.js");
9
+ /**
10
+ * Plugin and adapter libraries start from the class the `plugin` / `adapter` generators write,
11
+ * so there is one template per class instead of two that drift apart.
12
+ */
13
+ const CLASS_GENERATOR = {
14
+ plugin: 'plugin',
15
+ adapter: 'adapter',
16
+ };
17
+ /**
18
+ * A publishable library is published from its own folder after its `build` target compiles
19
+ * `tsconfig.lib.json` into `dist/`, so the manifest points there and lists what the emitted JavaScript
20
+ * requires: tslib (the base config sets `importHelpers`) and, for the FrontMCP library types, the SDK.
21
+ * Both use the workspace's range when it declares one.
22
+ */
23
+ function writePublishablePackageJson(tree, options) {
24
+ const workspace = tree.exists('package.json') ? (0, devkit_1.readJson)(tree, 'package.json') : {};
25
+ const workspaceRange = (name) => workspace.dependencies?.[name] ?? workspace.devDependencies?.[name];
26
+ (0, devkit_1.writeJson)(tree, `${options.projectRoot}/package.json`, {
27
+ name: options.importPath,
28
+ version: '0.0.1',
29
+ type: 'commonjs',
30
+ main: './dist/index.js',
31
+ types: './dist/index.d.ts',
32
+ files: ['dist'],
33
+ dependencies: {
34
+ ...(options.libType !== 'generic' && {
35
+ '@frontmcp/sdk': workspaceRange('@frontmcp/sdk') ?? `~${(0, versions_js_1.getFrontmcpVersion)()}`,
36
+ }),
37
+ tslib: workspaceRange('tslib') ?? '^2.3.0',
38
+ },
39
+ });
40
+ }
7
41
  async function libGenerator(tree, schema) {
8
42
  return libGeneratorInternal(tree, schema);
9
43
  }
10
44
  async function libGeneratorInternal(tree, schema) {
11
45
  const options = (0, index_js_1.normalizeOptions)(tree, schema);
46
+ const substitutions = { ...options, tmpl: '' };
12
47
  // Generate project config files (project.json, tsconfig, jest)
13
- (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'lib-project-files'), options.projectRoot, {
14
- ...options,
15
- tmpl: '',
16
- });
17
- // Generate type-specific source files
18
- const templateDir = (0, path_1.join)(__dirname, 'files', options.libType);
19
- (0, devkit_1.generateFiles)(tree, templateDir, options.projectRoot, {
20
- ...options,
21
- tmpl: '',
22
- });
23
- // Add path mapping to tsconfig.base.json if it exists
24
- if (tree.exists('tsconfig.base.json')) {
25
- (0, devkit_1.updateJson)(tree, 'tsconfig.base.json', (json) => {
26
- json.compilerOptions = json.compilerOptions ?? {};
27
- json.compilerOptions.paths = json.compilerOptions.paths ?? {};
28
- json.compilerOptions.paths[options.importPath] = [`${options.projectRoot}/src/index.ts`];
29
- return json;
30
- });
48
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'lib-project-files'), options.projectRoot, substitutions);
49
+ // Generate type-specific source files (barrel and starter spec)
50
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files', options.libType), options.projectRoot, substitutions);
51
+ const classGenerator = CLASS_GENERATOR[options.libType];
52
+ if (classGenerator) {
53
+ const srcDir = (0, path_1.join)(options.projectRoot, 'src');
54
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, '..', classGenerator, 'files'), srcDir, substitutions);
55
+ // The plugin generator's opt-in context extension is not part of a new library.
56
+ const contextExtension = (0, path_1.join)(srcDir, `${options.fileName}.context-extension.ts`);
57
+ if (tree.exists(contextExtension))
58
+ tree.delete(contextExtension);
59
+ }
60
+ if (options.publishable) {
61
+ writePublishablePackageJson(tree, options);
31
62
  }
63
+ (0, project_paths_js_1.addTsPathAlias)(tree, options.importPath, `${options.projectRoot}/src/index.ts`);
32
64
  if (!options.skipFormat) {
33
65
  await (0, devkit_1.formatFiles)(tree);
34
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../../../src/generators/lib/lib.ts"],"names":[],"mappings":";;AAWA,oCAEC;AAbD,uCAMoB;AACpB,+BAA4B;AAE5B,6CAAkD;AAE3C,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,MAA0B;IACvE,OAAO,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,IAAU,EACV,MAA0B;IAE1B,MAAM,OAAO,GAAG,IAAA,2BAAgB,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE/C,+DAA+D;IAC/D,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAA,WAAI,EAAC,SAAS,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE;QAC7E,GAAG,OAAO;QACV,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,WAAW,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAA,sBAAa,EAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;QACpD,GAAG,OAAO;QACV,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IAEH,sDAAsD;IACtD,IAAI,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACtC,IAAA,mBAAU,EAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC;YAClD,IAAI,CAAC,eAAe,CAAC,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,eAAe,CAAC,CAAC;YACzF,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,kBAAe,YAAY,CAAC","sourcesContent":["import {\n type Tree,\n formatFiles,\n generateFiles,\n updateJson,\n type GeneratorCallback,\n} from '@nx/devkit';\nimport { join } from 'path';\nimport type { LibGeneratorSchema } from './schema.js';\nimport { normalizeOptions } from './lib/index.js';\n\nexport async function libGenerator(tree: Tree, schema: LibGeneratorSchema): Promise<GeneratorCallback | void> {\n return libGeneratorInternal(tree, schema);\n}\n\nasync function libGeneratorInternal(\n tree: Tree,\n schema: LibGeneratorSchema,\n): Promise<GeneratorCallback | void> {\n const options = normalizeOptions(tree, schema);\n\n // Generate project config files (project.json, tsconfig, jest)\n generateFiles(tree, join(__dirname, 'lib-project-files'), options.projectRoot, {\n ...options,\n tmpl: '',\n });\n\n // Generate type-specific source files\n const templateDir = join(__dirname, 'files', options.libType);\n generateFiles(tree, templateDir, options.projectRoot, {\n ...options,\n tmpl: '',\n });\n\n // Add path mapping to tsconfig.base.json if it exists\n if (tree.exists('tsconfig.base.json')) {\n updateJson(tree, 'tsconfig.base.json', (json) => {\n json.compilerOptions = json.compilerOptions ?? {};\n json.compilerOptions.paths = json.compilerOptions.paths ?? {};\n json.compilerOptions.paths[options.importPath] = [`${options.projectRoot}/src/index.ts`];\n return json;\n });\n }\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n}\n\nexport default libGenerator;\n"]}
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../../../src/generators/lib/lib.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'path';\n\nimport { formatFiles, generateFiles, readJson, writeJson, type GeneratorCallback, type Tree } from '@nx/devkit';\n\nimport { addTsPathAlias } from '../../utils/project-paths.js';\nimport { getFrontmcpVersion } from '../../utils/versions.js';\nimport { normalizeOptions, type NormalizedLibOptions } from './lib/index.js';\nimport type { LibGeneratorSchema } from './schema.js';\n\n/**\n * Plugin and adapter libraries start from the class the `plugin` / `adapter` generators write,\n * so there is one template per class instead of two that drift apart.\n */\nconst CLASS_GENERATOR: Partial<Record<NormalizedLibOptions['libType'], string>> = {\n plugin: 'plugin',\n adapter: 'adapter',\n};\n\ninterface WorkspacePackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * A publishable library is published from its own folder after its `build` target compiles\n * `tsconfig.lib.json` into `dist/`, so the manifest points there and lists what the emitted JavaScript\n * requires: tslib (the base config sets `importHelpers`) and, for the FrontMCP library types, the SDK.\n * Both use the workspace's range when it declares one.\n */\nfunction writePublishablePackageJson(tree: Tree, options: NormalizedLibOptions): void {\n const workspace = tree.exists('package.json') ? readJson<WorkspacePackageJson>(tree, 'package.json') : {};\n const workspaceRange = (name: string): string | undefined =>\n workspace.dependencies?.[name] ?? workspace.devDependencies?.[name];\n\n writeJson(tree, `${options.projectRoot}/package.json`, {\n name: options.importPath,\n version: '0.0.1',\n type: 'commonjs',\n main: './dist/index.js',\n types: './dist/index.d.ts',\n files: ['dist'],\n dependencies: {\n ...(options.libType !== 'generic' && {\n '@frontmcp/sdk': workspaceRange('@frontmcp/sdk') ?? `~${getFrontmcpVersion()}`,\n }),\n tslib: workspaceRange('tslib') ?? '^2.3.0',\n },\n });\n}\n\nexport async function libGenerator(tree: Tree, schema: LibGeneratorSchema): Promise<GeneratorCallback | void> {\n return libGeneratorInternal(tree, schema);\n}\n\nasync function libGeneratorInternal(tree: Tree, schema: LibGeneratorSchema): Promise<GeneratorCallback | void> {\n const options = normalizeOptions(tree, schema);\n const substitutions = { ...options, tmpl: '' };\n\n // Generate project config files (project.json, tsconfig, jest)\n generateFiles(tree, join(__dirname, 'lib-project-files'), options.projectRoot, substitutions);\n\n // Generate type-specific source files (barrel and starter spec)\n generateFiles(tree, join(__dirname, 'files', options.libType), options.projectRoot, substitutions);\n\n const classGenerator = CLASS_GENERATOR[options.libType];\n if (classGenerator) {\n const srcDir = join(options.projectRoot, 'src');\n generateFiles(tree, join(__dirname, '..', classGenerator, 'files'), srcDir, substitutions);\n // The plugin generator's opt-in context extension is not part of a new library.\n const contextExtension = join(srcDir, `${options.fileName}.context-extension.ts`);\n if (tree.exists(contextExtension)) tree.delete(contextExtension);\n }\n\n if (options.publishable) {\n writePublishablePackageJson(tree, options);\n }\n\n addTsPathAlias(tree, options.importPath, `${options.projectRoot}/src/index.ts`);\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n}\n\nexport default libGenerator;\n"]}
@@ -8,13 +8,27 @@
8
8
  "build": {
9
9
  "executor": "@frontmcp/nx:build",
10
10
  "cache": true,
11
- "outputs": ["{projectRoot}/dist"],
11
+ "outputs": [<%- buildOutputs.map(o => `"${o}"`).join(', ') %>],
12
12
  "options": {
13
13
  "entry": "{projectRoot}/src/main.ts",
14
14
  "outputPath": "{projectRoot}/dist",
15
15
  "target": "<%= deploymentTarget %>"
16
16
  }
17
17
  },
18
+ "dev": {
19
+ "executor": "@frontmcp/nx:dev",
20
+ "options": {
21
+ "entry": "{projectRoot}/src/main.ts"
22
+ }
23
+ },
24
+ "typecheck": {
25
+ "executor": "nx:run-commands",
26
+ "cache": true,
27
+ "options": {
28
+ "command": "tsc --noEmit -p tsconfig.lib.json",
29
+ "cwd": "{projectRoot}"
30
+ }
31
+ },
18
32
  "deploy": {
19
33
  "executor": "@frontmcp/nx:deploy",
20
34
  "dependsOn": ["build"],
@@ -2,11 +2,16 @@
2
2
  "extends": "<%= offset %>tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "module": "commonjs",
5
- "moduleResolution": "node10",<% if (ignoreDeprecations) { %>
6
- "ignoreDeprecations": "<%= ignoreDeprecations %>",<% } %>
5
+ "moduleResolution": "<%= moduleResolution %>",<% if (resetCustomConditions) { %>
6
+ "customConditions": null,<% } %>
7
+ "rootDir": "<%= offset %>",
8
+ "composite": false,
9
+ "declarationMap": false,
10
+ "emitDeclarationOnly": false,
7
11
  "emitDecoratorMetadata": true,
8
12
  "experimentalDecorators": true
9
13
  },
10
14
  "include": ["src/**/*.ts"],
11
- "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
15
+ "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"],
16
+ "nx": { "addTypecheckTarget": false }
12
17
  }
@@ -12,8 +12,10 @@ Resources:
12
12
  McpFunction:
13
13
  Type: AWS::Serverless::Function
14
14
  Properties:
15
- CodeUri: dist/
16
- Handler: main.handler
15
+ # `nx build <%= projectName %>` (target lambda) writes dist/lambda/handler.cjs, which exports `handler`.
16
+ # The bundle requires @codegenie/serverless-express at runtime without bundling it: add a Lambda layer that provides it.
17
+ CodeUri: dist/lambda/
18
+ Handler: handler.handler
17
19
  Events:
18
20
  Api:
19
21
  Type: HttpApi
@@ -0,0 +1,10 @@
1
+ # Docker reads this file for the Dockerfile next to it: the build context is the workspace root.
2
+ **/node_modules
3
+ **/dist
4
+ **/.vercel
5
+ .nx
6
+ tmp
7
+ coverage
8
+ .git
9
+ **/.env
10
+ **/.env.*
@@ -1,3 +1,4 @@
1
+ # Build from the workspace root: docker build -f <%= projectRoot %>/Dockerfile .
1
2
  FROM node:24-slim AS builder
2
3
  WORKDIR /app
3
4
  COPY package*.json ./
@@ -8,10 +9,14 @@ RUN npx nx build <%= projectName %>
8
9
  FROM node:24-slim AS runner
9
10
  WORKDIR /app
10
11
  ENV NODE_ENV=production
12
+ # A production server needs MCP_SESSION_SECRET at runtime: docker run -e MCP_SESSION_SECRET=$(openssl rand -hex 32) ...
13
+ # The server binds 127.0.0.1 by default, which a published port cannot reach.
14
+ ENV FRONTMCP_BIND_ADDRESS=all
11
15
  RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
12
16
  COPY --from=builder /app/<%= projectRoot %>/dist ./dist
13
17
  COPY --from=builder /app/package.json /app/package-lock.json* ./
14
18
  RUN npm ci --omit=dev --ignore-scripts
15
19
  USER appuser
16
20
  EXPOSE 3000
17
- CMD ["node", "dist/main.js"]
21
+ # `nx build <%= projectName %>` (target node) writes dist/node/<%= projectName %>.bundle.js
22
+ CMD ["node", "dist/node/<%= projectName %>.bundle.js"]
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  app:
3
3
  build:
4
- context: ../..
4
+ context: <%= offset %>
5
5
  dockerfile: <%= projectRoot %>/Dockerfile
6
6
  ports:
7
7
  - '${PORT:-3000}:3000'
@@ -1,15 +1,6 @@
1
1
  {
2
+ "$schema": "https://openapi.vercel.sh/vercel.json",
2
3
  "version": 2,
3
- "builds": [
4
- {
5
- "src": "<%= projectRoot %>/dist/main.js",
6
- "use": "@vercel/node"
7
- }
8
- ],
9
- "routes": [
10
- {
11
- "src": "/(.*)",
12
- "dest": "<%= projectRoot %>/dist/main.js"
13
- }
14
- ]
4
+ "installCommand": "cd <%= offset %> && <%= packageManager.install %>",
5
+ "buildCommand": "cd <%= offset %> && <%= packageManager.exec %> nx build <%= projectName %>"
15
6
  }
@@ -1,11 +1,14 @@
1
1
  import { type Tree } from '@nx/devkit';
2
2
  import type { ServerGeneratorSchema } from '../schema.js';
3
+ import { type PackageManagerCommands } from '../../../utils/package-manager.js';
4
+ import { type ProjectModuleResolution } from '../../../utils/project-paths.js';
3
5
  export interface NormalizedServerOptions {
4
6
  name: string;
5
7
  projectName: string;
6
8
  projectRoot: string;
7
9
  offset: string;
8
- ignoreDeprecations: string;
10
+ moduleResolution: ProjectModuleResolution;
11
+ resetCustomConditions: boolean;
9
12
  className: string;
10
13
  fileName: string;
11
14
  deploymentTarget: 'node' | 'vercel' | 'lambda' | 'cloudflare';
@@ -16,6 +19,10 @@ export interface NormalizedServerOptions {
16
19
  importPath: string;
17
20
  }>;
18
21
  redis: 'docker' | 'existing' | 'none';
22
+ /** What the build target writes and Nx caches: `dist/`, plus the Build Output API tree for vercel. */
23
+ buildOutputs: string[];
24
+ /** Commands of the workspace's package manager, for the deployment config files. */
25
+ packageManager: PackageManagerCommands;
19
26
  parsedTags: string[];
20
27
  skipFormat: boolean;
21
28
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeOptions = normalizeOptions;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const path_1 = require("path");
6
+ const package_manager_js_1 = require("../../../utils/package-manager.js");
6
7
  const project_paths_js_1 = require("../../../utils/project-paths.js");
7
8
  function normalizeOptions(tree, schema) {
8
9
  const { className, fileName } = (0, devkit_1.names)(schema.name);
@@ -19,18 +20,23 @@ function normalizeOptions(tree, schema) {
19
20
  const importPath = path_1.posix.relative((0, devkit_1.joinPathFragments)(projectRoot, 'src'), (0, devkit_1.joinPathFragments)(appSourceRoot, `${appFile}.app`));
20
21
  return { className: `${appClass}App`, importPath: importPath.startsWith('.') ? importPath : `./${importPath}` };
21
22
  });
23
+ const deploymentTarget = schema.deploymentTarget ?? 'node';
24
+ // `frontmcp build --target vercel` writes `.vercel/output` (Build Output API) in the project folder.
25
+ const buildOutputs = deploymentTarget === 'vercel' ? ['{projectRoot}/dist', '{projectRoot}/.vercel/output'] : ['{projectRoot}/dist'];
22
26
  return {
23
27
  name: schema.name,
24
28
  projectName: `server-${fileName}`,
25
29
  projectRoot,
26
30
  offset,
27
- ignoreDeprecations: (0, project_paths_js_1.getIgnoreDeprecations)(tree),
31
+ ...(0, project_paths_js_1.getProjectTsOptions)(tree),
28
32
  className,
29
33
  fileName,
30
- deploymentTarget: schema.deploymentTarget ?? 'node',
34
+ deploymentTarget,
31
35
  appNames,
32
36
  appImports,
33
37
  redis: schema.redis ?? 'none',
38
+ buildOutputs,
39
+ packageManager: (0, package_manager_js_1.getPackageManagerCommands)(tree),
34
40
  parsedTags,
35
41
  skipFormat: schema.skipFormat ?? false,
36
42
  };
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/server/lib/normalize-options.ts"],"names":[],"mappings":";;AAsBA,4CAiCC;AAvDD,uCAA8E;AAC9E,+BAA6B;AAE7B,sEAA6F;AAmB7F,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA6B;IACxE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChH,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAEtB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE7E,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1C,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAA,cAAK,EAAC,OAAO,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG,UAAU,EAAE,UAAU,IAAI,IAAA,0BAAiB,EAAC,UAAU,EAAE,IAAI,IAAI,IAAA,0BAAiB,EAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;QACjI,MAAM,UAAU,GAAG,YAAK,CAAC,QAAQ,CAAC,IAAA,0BAAiB,EAAC,WAAW,EAAE,KAAK,CAAC,EAAE,IAAA,0BAAiB,EAAC,aAAa,EAAE,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC;QAC7H,OAAO,EAAE,SAAS,EAAE,GAAG,QAAQ,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,EAAE,CAAC;IAClH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,UAAU,QAAQ,EAAE;QACjC,WAAW;QACX,MAAM;QACN,kBAAkB,EAAE,IAAA,wCAAqB,EAAC,IAAI,CAAC;QAC/C,SAAS;QACT,QAAQ;QACR,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,MAAM;QACnD,QAAQ;QACR,UAAU;QACV,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM;QAC7B,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, getProjects, joinPathFragments, names } from '@nx/devkit';\nimport { posix } from 'path';\nimport type { ServerGeneratorSchema } from '../schema.js';\nimport { getIgnoreDeprecations, resolveProjectPaths } from '../../../utils/project-paths.js';\n\nexport interface NormalizedServerOptions {\n name: string;\n projectName: string;\n projectRoot: string;\n offset: string;\n ignoreDeprecations: string;\n className: string;\n fileName: string;\n deploymentTarget: 'node' | 'vercel' | 'lambda' | 'cloudflare';\n appNames: string[];\n /** Each composed app with the import specifier `main.ts` uses, relative to the server's `src/`. */\n appImports: Array<{ className: string; importPath: string }>;\n redis: 'docker' | 'existing' | 'none';\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: ServerGeneratorSchema): NormalizedServerOptions {\n const { className, fileName } = names(schema.name);\n const { projectRoot, offset } = resolveProjectPaths(schema.directory ?? joinPathFragments('servers', fileName));\n const parsedTags = schema.tags\n ? schema.tags.split(',').map((t) => t.trim())\n : ['scope:servers'];\n\n const appNames = schema.apps.split(',').map((a) => a.trim()).filter(Boolean);\n\n const projects = getProjects(tree);\n const appImports = appNames.map((appName) => {\n const { className: appClass, fileName: appFile } = names(appName);\n const appProject = projects.get(appName) ?? projects.get(appFile);\n const appSourceRoot = appProject?.sourceRoot ?? joinPathFragments(appProject?.root ?? joinPathFragments('apps', appFile), 'src');\n const importPath = posix.relative(joinPathFragments(projectRoot, 'src'), joinPathFragments(appSourceRoot, `${appFile}.app`));\n return { className: `${appClass}App`, importPath: importPath.startsWith('.') ? importPath : `./${importPath}` };\n });\n\n return {\n name: schema.name,\n projectName: `server-${fileName}`,\n projectRoot,\n offset,\n ignoreDeprecations: getIgnoreDeprecations(tree),\n className,\n fileName,\n deploymentTarget: schema.deploymentTarget ?? 'node',\n appNames,\n appImports,\n redis: schema.redis ?? 'none',\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
1
+ {"version":3,"file":"normalize-options.js","sourceRoot":"","sources":["../../../../../src/generators/server/lib/normalize-options.ts"],"names":[],"mappings":";;AA4BA,4CAwCC;AApED,uCAA8E;AAC9E,+BAA6B;AAE7B,0EAA2G;AAC3G,sEAAyH;AAwBzH,SAAgB,gBAAgB,CAAC,IAAU,EAAE,MAA6B;IACxE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAmB,EAAC,MAAM,CAAC,SAAS,IAAI,IAAA,0BAAiB,EAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChH,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAEtB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE7E,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1C,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAA,cAAK,EAAC,OAAO,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG,UAAU,EAAE,UAAU,IAAI,IAAA,0BAAiB,EAAC,UAAU,EAAE,IAAI,IAAI,IAAA,0BAAiB,EAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;QACjI,MAAM,UAAU,GAAG,YAAK,CAAC,QAAQ,CAAC,IAAA,0BAAiB,EAAC,WAAW,EAAE,KAAK,CAAC,EAAE,IAAA,0BAAiB,EAAC,aAAa,EAAE,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC;QAC7H,OAAO,EAAE,SAAS,EAAE,GAAG,QAAQ,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,EAAE,CAAC;IAClH,CAAC,CAAC,CAAC;IAEH,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,MAAM,CAAC;IAC3D,qGAAqG;IACrG,MAAM,YAAY,GAChB,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,oBAAoB,EAAE,8BAA8B,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAElH,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,UAAU,QAAQ,EAAE;QACjC,WAAW;QACX,MAAM;QACN,GAAG,IAAA,sCAAmB,EAAC,IAAI,CAAC;QAC5B,SAAS;QACT,QAAQ;QACR,gBAAgB;QAChB,QAAQ;QACR,UAAU;QACV,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM;QAC7B,YAAY;QACZ,cAAc,EAAE,IAAA,8CAAyB,EAAC,IAAI,CAAC;QAC/C,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { type Tree, getProjects, joinPathFragments, names } from '@nx/devkit';\nimport { posix } from 'path';\nimport type { ServerGeneratorSchema } from '../schema.js';\nimport { getPackageManagerCommands, type PackageManagerCommands } from '../../../utils/package-manager.js';\nimport { getProjectTsOptions, resolveProjectPaths, type ProjectModuleResolution } from '../../../utils/project-paths.js';\n\nexport interface NormalizedServerOptions {\n name: string;\n projectName: string;\n projectRoot: string;\n offset: string;\n moduleResolution: ProjectModuleResolution;\n resetCustomConditions: boolean;\n className: string;\n fileName: string;\n deploymentTarget: 'node' | 'vercel' | 'lambda' | 'cloudflare';\n appNames: string[];\n /** Each composed app with the import specifier `main.ts` uses, relative to the server's `src/`. */\n appImports: Array<{ className: string; importPath: string }>;\n redis: 'docker' | 'existing' | 'none';\n /** What the build target writes and Nx caches: `dist/`, plus the Build Output API tree for vercel. */\n buildOutputs: string[];\n /** Commands of the workspace's package manager, for the deployment config files. */\n packageManager: PackageManagerCommands;\n parsedTags: string[];\n skipFormat: boolean;\n}\n\nexport function normalizeOptions(tree: Tree, schema: ServerGeneratorSchema): NormalizedServerOptions {\n const { className, fileName } = names(schema.name);\n const { projectRoot, offset } = resolveProjectPaths(schema.directory ?? joinPathFragments('servers', fileName));\n const parsedTags = schema.tags\n ? schema.tags.split(',').map((t) => t.trim())\n : ['scope:servers'];\n\n const appNames = schema.apps.split(',').map((a) => a.trim()).filter(Boolean);\n\n const projects = getProjects(tree);\n const appImports = appNames.map((appName) => {\n const { className: appClass, fileName: appFile } = names(appName);\n const appProject = projects.get(appName) ?? projects.get(appFile);\n const appSourceRoot = appProject?.sourceRoot ?? joinPathFragments(appProject?.root ?? joinPathFragments('apps', appFile), 'src');\n const importPath = posix.relative(joinPathFragments(projectRoot, 'src'), joinPathFragments(appSourceRoot, `${appFile}.app`));\n return { className: `${appClass}App`, importPath: importPath.startsWith('.') ? importPath : `./${importPath}` };\n });\n\n const deploymentTarget = schema.deploymentTarget ?? 'node';\n // `frontmcp build --target vercel` writes `.vercel/output` (Build Output API) in the project folder.\n const buildOutputs =\n deploymentTarget === 'vercel' ? ['{projectRoot}/dist', '{projectRoot}/.vercel/output'] : ['{projectRoot}/dist'];\n\n return {\n name: schema.name,\n projectName: `server-${fileName}`,\n projectRoot,\n offset,\n ...getProjectTsOptions(tree),\n className,\n fileName,\n deploymentTarget,\n appNames,\n appImports,\n redis: schema.redis ?? 'none',\n buildOutputs,\n packageManager: getPackageManagerCommands(tree),\n parsedTags,\n skipFormat: schema.skipFormat ?? false,\n };\n}\n"]}
@@ -5,6 +5,8 @@ const tslib_1 = require("tslib");
5
5
  const fs = tslib_1.__importStar(require("fs"));
6
6
  const path_1 = require("path");
7
7
  const devkit_1 = require("@nx/devkit");
8
+ const add_dependencies_js_1 = require("../../utils/add-dependencies.js");
9
+ const versions_js_1 = require("../../utils/versions.js");
8
10
  const index_js_1 = require("./lib/index.js");
9
11
  async function serverGenerator(tree, schema) {
10
12
  return serverGeneratorInternal(tree, schema);
@@ -27,9 +29,14 @@ async function serverGeneratorInternal(tree, schema) {
27
29
  if (bundle !== 'none') {
28
30
  scaffoldCatalogSkills(tree, options.projectRoot, options.deploymentTarget, bundle);
29
31
  }
32
+ // The lambda build wraps the server with @codegenie/serverless-express and fails without it.
33
+ const installTask = options.deploymentTarget === 'lambda'
34
+ ? (0, add_dependencies_js_1.addFrontmcpDependencies)(tree, (0, versions_js_1.getLambdaDependencies)(), {}, { keepExistingVersions: true })
35
+ : undefined;
30
36
  if (!options.skipFormat) {
31
37
  await (0, devkit_1.formatFiles)(tree);
32
38
  }
39
+ return installTask;
33
40
  }
34
41
  function scaffoldCatalogSkills(tree, projectRoot, target, bundle) {
35
42
  // Load skills catalog via @frontmcp/skills package at runtime