Configurer un monorepo
Tâche 1 : Créer un monorepo
Section intitulée « Tâche 1 : Créer un monorepo »Pour créer un nouveau monorepo, depuis le répertoire de votre choix, exécutez la commande suivante :
npx create-nx-workspace@21.6.5 dungeon-adventure --pm=pnpm  --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=yarn  --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=npm  --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=bun  --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsCela configurera un monorepo NX dans le répertoire dungeon-adventure. Lorsque vous ouvrez le répertoire dans VSCode, vous verrez cette structure de fichiers :
- Répertoire.nx/- …
 
- Répertoire.vscode/- …
 
- Répertoirenode_modules/- …
 
- Répertoirepackages/ c’est ici que résideront vos sous-projets- …
 
- .gitignore
- .npmrc
- .prettierignore
- .prettierrc
- nx.json configure le CLI Nx et les paramètres par défaut du monorepo
- package.json toutes les dépendances Node sont définies ici
- pnpm-lock.yaml ou bun.lock, yarn.lock, package-lock.json selon le gestionnaire de paquets
- pnpm-workspace.yaml si utilisation de pnpm
- README.md
- tsconfig.base.json tous les sous-projets Node étendent ce fichier
- tsconfig.json
- aws-nx-plugin.config.mts configuration du plugin Nx pour AWS
Nous pouvons maintenant commencer à créer nos différents sous-projets en utilisant le @aws/nx-plugin.
Tâche 2 : Créer une API de jeu
Section intitulée « Tâche 2 : Créer une API de jeu »Tout d’abord, créons notre API de jeu. Pour ce faire, créez une API tRPC appelée GameApi en suivant ces étapes :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#trpc-api
- Remplissez les paramètres requis- name: GameApi
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactiveyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivenpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivebunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître dans votre arborescence de fichiers.
Fichiers modifiés par ts#trpc-api
Voici la liste de tous les fichiers générés par le générateur ts#trpc-api. Nous allons examiner certains des fichiers clés mis en évidence dans l’arborescence :
- Répertoirepackages/- Répertoirecommon/- Répertoireconstructs/- Répertoiresrc/- Répertoireapp/ constructs CDK spécifiques à l’application- Répertoireapis/- game-api.ts construct CDK pour créer votre API tRPC
- index.ts
- …
 
- index.ts
 
- Répertoirecore/ constructs CDK génériques- Répertoireapi/- rest-api.ts construct CDK de base pour une API Gateway Rest API
- trpc-utils.ts utilitaires pour les constructs CDK d’API tRPC
- utils.ts utilitaires pour les constructs d’API
 
- index.ts
- runtime-config.ts
 
- index.ts
 
- project.json
- …
 
- Répertoiretypes/ types partagés- Répertoiresrc/- index.ts
- runtime-config.ts définition d’interface utilisée par CDK et le site web
 
- project.json
- …
 
 
- Répertoiregame-api/ API tRPC- Répertoiresrc/- Répertoireclient/ client vanilla typiquement utilisé pour les appels TypeScript machine à machine- index.ts
- sigv4.ts
 
- Répertoiremiddleware/ instrumentation Powertools- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
 
- Répertoireschema/ définitions des entrées et sorties pour votre API- echo.ts
 
- Répertoireprocedures/ implémentations spécifiques pour vos procédures/routes d’API- echo.ts
 
- index.ts
- init.ts configure le contexte et le middleware
- local-server.ts utilisé lors de l’exécution du serveur tRPC localement
- router.ts point d’entrée pour votre handler Lambda qui définit toutes les procédures
 
- project.json
- …
 
 
- eslint.config.mjs
- vitest.workspace.ts
Examinons ces fichiers clés :
import {  awsLambdaRequestHandler,  CreateAWSLambdaContextOptions,} from '@trpc/server/adapters/aws-lambda';import { echo } from './procedures/echo.js';import { t } from './init.js';import { APIGatewayProxyEvent } from 'aws-lambda';
export const router = t.router;
export const appRouter = router({  echo,});
export const handler = awsLambdaRequestHandler({  router: appRouter,  createContext: (    ctx: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>,  ) => ctx,  responseMeta: () => ({    headers: {      'Access-Control-Allow-Origin': '*',      'Access-Control-Allow-Methods': '*',    },  }),});
export type AppRouter = typeof appRouter;Le routeur définit le point d’entrée de votre API tRPC et c’est l’endroit où vous déclarerez toutes vos méthodes d’API. Comme vous pouvez le voir ci-dessus, nous avons une méthode appelée echo avec son implémentation dans le fichier ./procedures/echo.ts.
import { publicProcedure } from '../init.js';import {  EchoInputSchema,  EchoOutputSchema,} from '../schema/echo.js';
export const echo = publicProcedure  .input(EchoInputSchema)  .output(EchoOutputSchema)  .query((opts) => ({ result: opts.input.message }));Ce fichier est l’implémentation de la méthode echo et comme vous pouvez le voir, elle est fortement typée en déclarant ses structures de données d’entrée et de sortie.
import { z } from 'zod';
export const EchoInputSchema = z.object({  message: z.string(),});
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
export const EchoOutputSchema = z.object({  result: z.string(),});
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;Toutes les définitions de schéma tRPC sont définies en utilisant Zod et sont exportées en tant que types TypeScript via la syntaxe z.TypeOf.
import { Construct } from 'constructs';import * as url from 'url';import {  Code,  Runtime,  Function,  FunctionProps,  Tracing,} from 'aws-cdk-lib/aws-lambda';import {  AuthorizationType,  Cors,  LambdaIntegration,} from 'aws-cdk-lib/aws-apigateway';import { Duration, Stack } from 'aws-cdk-lib';import {  PolicyDocument,  PolicyStatement,  Effect,  AccountPrincipal,  AnyPrincipal,} from 'aws-cdk-lib/aws-iam';import {  IntegrationBuilder,  RestApiIntegration,} from '../../core/api/utils.js';import { RestApi } from '../../core/api/rest-api.js';import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';import { AppRouter, appRouter } from ':dungeon-adventure/game-api';
// Type d'union de chaînes pour tous les noms d'opérations d'APItype Operations = Procedures<AppRouter>;
/** * Propriétés pour créer un construct GameApi * * @template TIntegrations - Map des noms d'opérations vers leurs intégrations */export interface GameApiProps<  TIntegrations extends Record<Operations, RestApiIntegration>,> {  /**   * Map des noms d'opérations vers leurs intégrations API Gateway   */  integrations: TIntegrations;}
/** * Un construct CDK qui crée et configure une API REST AWS API Gateway * spécifiquement pour GameApi. * @template TIntegrations - Map des noms d'opérations vers leurs intégrations */export class GameApi<  TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> {  /**   * Crée des intégrations par défaut pour toutes les opérations, qui implémentent chaque opération en tant   * que sa propre fonction Lambda individuelle.   *   * @param scope - La portée du construct CDK   * @returns Un IntegrationBuilder avec des intégrations Lambda par défaut   */  public static defaultIntegrations = (scope: Construct) => {    return IntegrationBuilder.rest({      operations: routerToOperations(appRouter),      defaultIntegrationOptions: {        runtime: Runtime.NODEJS_LATEST,        handler: 'index.handler',        code: Code.fromAsset(          url.fileURLToPath(            new URL(              '../../../../../../dist/packages/game-api/bundle',              import.meta.url,            ),          ),        ),        timeout: Duration.seconds(30),        tracing: Tracing.ACTIVE,        environment: {          AWS_CONNECTION_REUSE_ENABLED: '1',        },      } satisfies FunctionProps,      buildDefaultIntegration: (op, props: FunctionProps) => {        const handler = new Function(scope, `GameApi${op}Handler`, props);        return { handler, integration: new LambdaIntegration(handler) };      },    });  };
  constructor(    scope: Construct,    id: string,    props: GameApiProps<TIntegrations>,  ) {    super(scope, id, {      apiName: 'GameApi',      defaultMethodOptions: {        authorizationType: AuthorizationType.IAM,      },      defaultCorsPreflightOptions: {        allowOrigins: Cors.ALL_ORIGINS,        allowMethods: Cors.ALL_METHODS,      },      policy: new PolicyDocument({        statements: [          // Ici, nous accordons à toutes les informations d'identification AWS du compte dans lequel le projet est déployé          // le droit d'appeler l'API. Un accès machine à machine plus fin peut être défini ici en utilisant des principaux          // plus spécifiques (par exemple, des rôles ou des utilisateurs) et des ressources (par exemple, quels chemins d'API          // peuvent être invoqués par quel principal) si nécessaire.          new PolicyStatement({            effect: Effect.ALLOW,            principals: [new AccountPrincipal(Stack.of(scope).account)],            actions: ['execute-api:Invoke'],            resources: ['execute-api:/*'],          }),          // Ouvrir OPTIONS pour permettre aux navigateurs de faire des requêtes de pré-vérification non authentifiées          new PolicyStatement({            effect: Effect.ALLOW,            principals: [new AnyPrincipal()],            actions: ['execute-api:Invoke'],            resources: ['execute-api:/*/OPTIONS/*'],          }),        ],      }),      operations: routerToOperations(appRouter),      ...props,    });  }}C’est le construct CDK qui définit notre GameApi. Il fournit une méthode defaultIntegrations qui crée automatiquement une fonction Lambda pour chaque procédure de notre API tRPC, pointant vers l’implémentation d’API empaquetée. Cela signifie qu’au moment de cdk synth, l’empaquetage ne se produit pas (contrairement à l’utilisation de NodeJsFunction) car nous l’avons déjà empaqueté dans le cadre de la cible de build du projet backend.
Tâche 3 : Créer des agents de narration
Section intitulée « Tâche 3 : Créer des agents de narration »Créons maintenant nos agents de narration.
Agent de narration : Projet Python
Section intitulée « Agent de narration : Projet Python »Pour créer un projet Python :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - py#project
- Remplissez les paramètres requis- name: story
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactiveyarn nx g @aws/nx-plugin:py#project --name=story --no-interactivenpx nx g @aws/nx-plugin:py#project --name=story --no-interactivebunx nx g @aws/nx-plugin:py#project --name=story --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître dans votre arborescence de fichiers.
Fichiers modifiés par py#project
Le générateur py#project génère ces fichiers :
- Répertoire.venv/ environnement virtuel unique pour le monorepo- …
 
- Répertoirepackages/- Répertoirestory/- Répertoiredungeon_adventure_story/ module Python- hello.py fichier Python d’exemple (nous l’ignorerons)
 
- Répertoiretests/- …
 
- .python-version
- pyproject.toml
- project.json
 
 
- .python-version version Python uv figée
- pyproject.toml
- uv.lock
Cela a configuré un projet Python et un UV Workspace avec un environnement virtuel partagé.
Agent de narration : Agent Strands
Section intitulée « Agent de narration : Agent Strands »Pour ajouter un agent Strands au projet avec le générateur py#strands-agent :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - py#strands-agent
- Remplissez les paramètres requis- project: story
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactiveyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivenpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivebunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître dans votre arborescence de fichiers.
Fichiers modifiés par py#strands-agent
Le générateur py#strands-agent génère ces fichiers :
- Répertoirepackages/- Répertoirestory/- Répertoiredungeon_adventure_story/ module Python- Répertoireagent/- main.py point d’entrée pour votre agent dans Bedrock AgentCore Runtime
- agent.py définit un exemple d’agent et d’outils
- agentcore_mcp_client.py utilitaire pour créer des clients pour interagir avec les serveurs MCP
- Dockerfile définit l’image Docker pour le déploiement sur AgentCore Runtime
 
 
 
- Répertoirecommon/constructs/- Répertoiresrc- Répertoirecore/agent-core/- runtime.ts construct générique pour le déploiement sur AgentCore Runtime
 
- Répertoireapp/agents/story-agent/- story-agent.ts construct pour déployer votre agent Story sur AgentCore Runtime
 
 
 
 
Examinons certains fichiers en détail :
from contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_time
# Définir un outil personnalisé@tooldef add(a: int, b: int) -> int:    return a + b
@contextmanagerdef get_agent(session_id: str):    yield Agent(        system_prompt="""Vous êtes un sorcier de l'addition.Utilisez l'outil 'add' pour les tâches d'addition.Référez-vous aux outils comme votre 'grimoire'.""",        tools=[add, current_time],    )Cela crée un exemple d’agent Strands et définit un outil d’addition.
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
@app.entrypointasync def invoke(payload, context):    """Handler pour l'invocation de l'agent"""    prompt = payload.get(        "prompt", "Aucun prompt trouvé dans l'entrée, veuillez guider l'utilisateur "        "à créer un payload JSON avec une clé prompt"    )
    with get_agent(session_id=context.session_id) as agent:        stream = agent.stream_async(prompt)        async for event in stream:            print(event)            yield (event)
if __name__ == "__main__":    app.run()C’est le point d’entrée de l’agent, configuré en utilisant le SDK Amazon Bedrock AgentCore. Il utilise le support de streaming de Strands et diffuse les événements au client au fur et à mesure qu’ils se produisent.
import { Lazy, Names } from 'aws-cdk-lib';import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';import { Construct } from 'constructs';import { execSync } from 'child_process';import * as path from 'path';import * as url from 'url';import {  AgentCoreRuntime,  AgentCoreRuntimeProps,} from '../../../core/agent-core/runtime.js';
export type StoryAgentProps = Omit<  AgentCoreRuntimeProps,  'runtimeName' | 'serverProtocol' | 'containerUri'>;
export class StoryAgent extends Construct {  public readonly dockerImage: DockerImageAsset;  public readonly agentCoreRuntime: AgentCoreRuntime;
  constructor(scope: Construct, id: string, props?: StoryAgentProps) {    super(scope, id);
    this.dockerImage = new DockerImageAsset(this, 'DockerImage', {      platform: Platform.LINUX_ARM64,      directory: path.dirname(url.fileURLToPath(new URL(import.meta.url))),      extraHash: execSync(        `docker inspect dungeon-adventure-story-agent:latest --format '{{.Id}}'`,        { encoding: 'utf-8' },      ).trim(),    });
    this.agentCoreRuntime = new AgentCoreRuntime(this, 'StoryAgent', {      runtimeName: Lazy.string({        produce: () =>          Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }),      }),      serverProtocol: 'HTTP',      containerUri: this.dockerImage.imageUri,      ...props,    });  }}Cela configure un DockerImageAsset CDK qui télécharge votre image Docker d’agent vers ECR et l’héberge en utilisant AgentCore Runtime.
Vous remarquerez peut-être un Dockerfile supplémentaire qui référence l’image Docker du projet story, nous permettant de co-localiser le Dockerfile et le code source de l’agent.
Tâche 4 : Configurer les outils d’inventaire
Section intitulée « Tâche 4 : Configurer les outils d’inventaire »Inventaire : Projet TypeScript
Section intitulée « Inventaire : Projet TypeScript »Créons un serveur MCP pour fournir des outils à notre agent Story afin de gérer l’inventaire d’un joueur.
Tout d’abord, nous créons un projet TypeScript :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#project
- Remplissez les paramètres requis- name: inventory
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runCela créera un projet TypeScript vide.
Fichiers modifiés par ts#project
Le générateur ts#project génère ces fichiers.
- Répertoirepackages/- Répertoireinventory/- Répertoiresrc/- index.ts point d’entrée avec fonction d’exemple
 
- project.json configuration du projet
- eslint.config.mjs configuration du linter
- vite.config.ts configuration des tests
- tsconfig.json configuration TypeScript de base pour le projet
- tsconfig.lib.json configuration TypeScript pour le projet ciblée pour la compilation et l’empaquetage
- tsconfig.spec.json configuration TypeScript pour les tests
 
 
- tsconfig.base.json mis à jour pour configurer un alias permettant aux autres projets de référencer celui-ci
Inventaire : Serveur MCP
Section intitulée « Inventaire : Serveur MCP »Ensuite, nous allons ajouter un serveur MCP à notre projet TypeScript :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#mcp-server
- Remplissez les paramètres requis- project: inventory
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runCela ajoutera un serveur MCP.
Fichiers modifiés par ts#mcp-server
Le générateur ts#mcp-server génère ces fichiers.
- Répertoirepackages/- Répertoireinventory/- Répertoiresrc/mcp-server/- server.ts crée le serveur MCP
- Répertoiretools/- add.ts outil d’exemple
 
- Répertoireresources/- sample-guidance.ts ressource d’exemple
 
- stdio.ts point d’entrée pour MCP avec transport STDIO
- http.ts point d’entrée pour MCP avec transport HTTP streamable
- Dockerfile construit l’image pour AgentCore Runtime
 
- rolldown.config.ts configuration pour empaqueter le serveur MCP pour le déploiement sur AgentCore
 
- Répertoirecommon/constructs/- Répertoiresrc- Répertoireapp/mcp-servers/inventory-mcp-server/- inventory-mcp-server.ts construct pour déployer votre serveur MCP d’inventaire sur AgentCore Runtime
 
 
 
 
Tâche 5 : Créer l’interface utilisateur (UI)
Section intitulée « Tâche 5 : Créer l’interface utilisateur (UI) »Dans cette tâche, nous allons créer l’interface utilisateur qui vous permettra d’interagir avec le jeu.
Interface utilisateur du jeu : Site web
Section intitulée « Interface utilisateur du jeu : Site web »Pour créer l’interface utilisateur, créez un site web appelé GameUI en suivant ces étapes :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#react-website
- Remplissez les paramètres requis- name: GameUI
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivenpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivebunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître dans votre arborescence de fichiers.
Fichiers modifiés par ts#react-website
Le générateur ts#react-website génère ces fichiers. Examinons certains des fichiers clés mis en évidence dans l’arborescence :
- Répertoirepackages/- Répertoirecommon/- Répertoireconstructs/- Répertoiresrc/- Répertoireapp/ constructs CDK spécifiques à l’application- Répertoirestatic-websites/- game-ui.ts construct CDK pour créer votre interface utilisateur du jeu
 
 
- Répertoirecore/- static-website.ts construct générique de site web statique
 
 
 
 
- Répertoiregame-ui/- Répertoirepublic/- …
 
- Répertoiresrc/- Répertoirecomponents/- RépertoireAppLayout/- index.ts mise en page globale : en-tête, pied de page, barre latérale, etc.
- navitems.ts éléments de navigation de la barre latérale
 
 
- Répertoirehooks/- useAppLayout.tsx vous permet de définir dynamiquement des éléments comme les notifications, le style de page, etc.
 
- Répertoireroutes/ routes basées sur des fichiers @tanstack/react-router- index.tsx page racine ’/’ redirige vers ‘/welcome’
- __root.tsx toutes les pages utilisent ce composant comme base
- Répertoirewelcome/- index.tsx
 
- config.ts
- main.tsx point d’entrée React
- routeTree.gen.ts automatiquement mis à jour par @tanstack/react-router
- styles.css
 
 
- index.html
- project.json
- vite.config.ts
- …
 
 
import * as url from 'url';import { Construct } from 'constructs';import { StaticWebsite } from '../../core/index.js';
export class GameUI extends StaticWebsite {  constructor(scope: Construct, id: string) {    super(scope, id, {      websiteName: 'GameUI',      websiteFilePath: url.fileURLToPath(        new URL(          '../../../../../../dist/packages/game-ui/bundle',          import.meta.url,        ),      ),    });  }}C’est le construct CDK qui définit notre GameUI. Il a déjà configuré le chemin de fichier vers le bundle généré pour notre interface utilisateur basée sur Vite. Cela signifie qu’au moment du build, l’empaquetage se produit dans la cible de build du projet game-ui et la sortie est utilisée ici.
import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';
import '@cloudscape-design/global-styles/index.css';
const router = createRouter({ routeTree });
// Enregistrer l'instance du routeur pour la sécurité des typesdeclare module '@tanstack/react-router' {  interface Register {    router: typeof router;  }}
const root = document.getElementById('root');root &&  createRoot(root).render(    <React.StrictMode>      <I18nProvider locale="en" messages={[messages]}>        <RouterProvider router={router} />      </I18nProvider>    </React.StrictMode>,  );C’est le point d’entrée où React est monté. Comme indiqué, il configure initialement simplement un @tanstack/react-router dans une configuration file-based-routing. Tant que votre serveur de développement est en cours d’exécution, vous pouvez créer des fichiers dans le dossier routes et @tanstack/react-router créera la configuration de fichier boilerplate pour vous, ainsi que la mise à jour du fichier routeTree.gen.ts. Ce fichier maintient toutes les routes de manière type-safe, ce qui signifie que lorsque vous utilisez <Link>, l’option to n’affichera que les routes valides.
Pour plus d’informations, consultez la documentation de @tanstack/react-router.
import {  ContentLayout,  Header,  SpaceBetween,  Container,} from '@cloudscape-design/components';import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/welcome/')({  component: RouteComponent,});
function RouteComponent() {  return (    <ContentLayout header={<Header>Welcome</Header>}>      <SpaceBetween size="l">        <Container>Welcome to your new Cloudscape website!</Container>      </SpaceBetween>    </ContentLayout>  );}Un composant sera rendu lors de la navigation vers la route /welcome. @tanstack/react-router gérera la Route pour vous chaque fois que vous créez/déplacez ce fichier (tant que le serveur de développement est en cours d’exécution).
Interface utilisateur du jeu : Authentification
Section intitulée « Interface utilisateur du jeu : Authentification »Configurons notre interface utilisateur du jeu pour exiger un accès authentifié via Amazon Cognito en suivant ces étapes :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#react-website#auth
- Remplissez les paramètres requis- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivenpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivebunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître/changer dans votre arborescence de fichiers.
Fichiers modifiés par ts#react-website#auth
Le générateur ts#react-website#auth met à jour/génère ces fichiers. Examinons certains des fichiers clés mis en évidence dans l’arborescence :
- Répertoirepackages/- Répertoirecommon/- Répertoireconstructs/- Répertoiresrc/- Répertoirecore/- user-identity.ts construct CDK pour créer des pools d’utilisateurs/d’identité
 
 
 
- Répertoiretypes/- Répertoiresrc/- runtime-config.ts mis à jour pour ajouter les cognitoProps
 
 
 
- Répertoiregame-ui/- Répertoiresrc/- Répertoirecomponents/- RépertoireAppLayout/- index.tsx ajoute l’utilisateur connecté/déconnexion à l’en-tête
 
- RépertoireCognitoAuth/- index.ts gère la connexion à Cognito
 
- RépertoireRuntimeConfig/- index.tsx récupère le runtime-config.jsonet le fournit aux enfants via le contexte
 
- index.tsx récupère le 
 
- Répertoirehooks/- useRuntimeConfig.tsx
 
- main.tsx Mis à jour pour ajouter Cognito
 
 
 
import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// Enregistrer l'instance du routeur pour la sécurité des typesdeclare module '@tanstack/react-router' {  interface Register {    router: typeof router;  }}const root = document.getElementById('root');root &&  createRoot(root).render(    <React.StrictMode>      <I18nProvider locale="en" messages={[messages]}>        <RuntimeConfigProvider>          <CognitoAuth>            <RouterProvider router={router} />          </CognitoAuth>        </RuntimeConfigProvider>      </I18nProvider>    </React.StrictMode>,  );Les composants RuntimeConfigProvider et CognitoAuth ont été ajoutés au fichier main.tsx via une transformation AST. Cela permet au composant CognitoAuth de s’authentifier avec Amazon Cognito en récupérant le runtime-config.json qui contient la configuration de connexion Cognito requise pour effectuer les appels backend vers la bonne destination.
Interface utilisateur du jeu : Connexion à l’API du jeu
Section intitulée « Interface utilisateur du jeu : Connexion à l’API du jeu »Configurons notre interface utilisateur du jeu pour se connecter à notre API GameApi créée précédemment.
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - api-connection
- Remplissez les paramètres requis- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivenpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivebunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runnpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runbunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître/changer dans votre arborescence de fichiers.
Fichiers modifiés par api-connection UI -> tRPC
Le générateur api-connection génère/met à jour ces fichiers. Examinons certains des fichiers clés mis en évidence dans l’arborescence :
- Répertoirepackages/- Répertoiregame-ui/- Répertoiresrc/- Répertoirecomponents/- GameApiClientProvider.tsx configure le client GameAPI
 
- Répertoirehooks/- useGameApi.tsx hooks pour appeler l’API GameApi
 
- main.tsx injecte les fournisseurs de client tRPC
 
 
 
- package.json
import { GameApiTRCPContext } from '../components/GameApiClientProvider';
export const useGameApi = GameApiTRCPContext.useTRPC;Ce hook utilise la dernière intégration React Query de tRPC permettant aux utilisateurs d’interagir avec @tanstack/react-query directement sans aucune couche d’abstraction supplémentaire. Pour des exemples sur la façon d’appeler des API tRPC, consultez le guide d’utilisation du hook tRPC.
import GameApiClientProvider from './components/GameApiClientProvider';import QueryClientProvider from './components/QueryClientProvider';import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// Enregistrer l'instance du routeur pour la sécurité des typesdeclare module '@tanstack/react-router' {  interface Register {    router: typeof router;  }}const root = document.getElementById('root');root &&  createRoot(root).render(    <React.StrictMode>      <I18nProvider locale="en" messages={[messages]}>        <RuntimeConfigProvider>          <CognitoAuth>            <QueryClientProvider>              <GameApiClientProvider>                <RouterProvider router={router} />              </GameApiClientProvider>            </QueryClientProvider>          </CognitoAuth>        </RuntimeConfigProvider>      </I18nProvider>    </React.StrictMode>,  );Le fichier main.tsx a été mis à jour via une transformation AST pour injecter les fournisseurs tRPC.
Interface utilisateur du jeu : Infrastructure
Section intitulée « Interface utilisateur du jeu : Infrastructure »Créons le sous-projet final pour l’infrastructure CDK.
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur Generate (UI)dans la section "Common Nx Commands"
- Recherchez @aws/nx-plugin - ts#infra
- Remplissez les paramètres requis- name: infra
 
- Cliquez sur Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactiveyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivenpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivebunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactiveVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runVous verrez de nouveaux fichiers apparaître/changer dans votre arborescence de fichiers.
Fichiers modifiés par ts#infra
Le générateur ts#infra génère/met à jour ces fichiers. Examinons certains des fichiers clés mis en évidence dans l’arborescence :
- Répertoirepackages/- Répertoirecommon/- Répertoireconstructs/- Répertoiresrc/- Répertoirecore/- checkov.ts
- index.ts
 
 
 
 
- Répertoireinfra- Répertoiresrc/- Répertoirestages/- application-stage.ts stacks CDK définis ici
 
- Répertoirestacks/- application-stack.ts ressources CDK définies ici
 
- index.ts
- main.ts point d’entrée qui définit toutes les étapes
 
- cdk.json
- project.json
- …
 
- package.json
- tsconfig.json ajoute des références
- tsconfig.base.json ajoute un alias
 
import { ApplicationStage } from './stacks/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
// Utilisez ceci pour déployer votre propre environnement sandbox (suppose vos informations d'identification CLI)new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', {  env: {    account: process.env.CDK_DEFAULT_ACCOUNT,    region: process.env.CDK_DEFAULT_REGION,  },});
app.synth();C’est le point d’entrée de votre application CDK.
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends cdk.Stack {  constructor(scope: Construct, id: string, props?: cdk.StackProps) {    super(scope, id, props);
    // Le code qui définit votre stack va ici  }}Instancions nos constructs CDK pour construire notre jeu d’aventure de donjon.
Tâche 6 : Mettre à jour notre infrastructure
Section intitulée « Tâche 6 : Mettre à jour notre infrastructure »Mettons à jour packages/infra/src/stacks/application-stack.ts pour instancier certains de nos constructs générés :
import {  GameApi,  GameUI,  InventoryMcpServer,  RuntimeConfig,  StoryAgent,  UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack {  constructor(scope: Construct, id: string, props?: StackProps) {    super(scope, id, props);
    const userIdentity = new UserIdentity(this, 'UserIdentity');
    const gameApi = new GameApi(this, 'GameApi', {      integrations: GameApi.defaultIntegrations(this).build(),    });
    const { userPool, userPoolClient } = userIdentity;
    const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
    // Use Cognito for user authentication with the agent    const storyAgent = new StoryAgent(this, 'StoryAgent', {      authorizerConfiguration: {        customJwtAuthorizer: {          discoveryUrl: `https://cognito-idp.${Stack.of(userPool).region}.amazonaws.com/${userPool.userPoolId}/.well-known/openid-configuration`,          allowedAudience: [userPoolClient.userPoolClientId],        },      },      environment: {        INVENTORY_MCP_ARN: mcpServer.agentCoreRuntime.arn,      },    });    // Add the Story Agent ARN to runtime-config.json so it can be referenced by the website    RuntimeConfig.ensure(this).config.agentArn =      storyAgent.agentCoreRuntime.arn;
    new CfnOutput(this, 'StoryAgentArn', {      value: storyAgent.agentCoreRuntime.arn,    });    new CfnOutput(this, 'InventoryMcpArn', {      value: mcpServer.agentCoreRuntime.arn,    });
    // Grant the agent permissions to invoke our mcp server    mcpServer.agentCoreRuntime.grantInvoke(storyAgent.agentCoreRuntime);
    // Grant the authenticated role access to invoke the api    gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
    // Ensure this is instantiated last so our runtime-config.json can be automatically configured    new GameUI(this, 'GameUI');  }}import { Stack, StackProps } from 'aws-cdk-lib';import {  GameApi,  GameUI,  InventoryMcpServer,  RuntimeConfig,  StoryAgent,  UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack {  constructor(scope: Construct, id: string, props?: StackProps) {    super(scope, id, props);
    // The code that defines your stack goes here    const userIdentity = new UserIdentity(this, 'UserIdentity');
    const gameApi = new GameApi(this, 'GameApi', {      integrations: GameApi.defaultIntegrations(this).build(),    });
    const { userPool, userPoolClient } = userIdentity;
    const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
    // Use Cognito for user authentication with the agent    const storyAgent = new StoryAgent(this, 'StoryAgent', {      authorizerConfiguration: {        customJwtAuthorizer: {          discoveryUrl: `https://cognito-idp.${Stack.of(userPool).region}.amazonaws.com/${userPool.userPoolId}/.well-known/openid-configuration`,          allowedAudience: [userPoolClient.userPoolClientId],        },      },      environment: {        INVENTORY_MCP_ARN: mcpServer.agentCoreRuntime.arn,      },    });    // Add the Story Agent ARN to runtime-config.json so it can be referenced by the website    RuntimeConfig.ensure(this).config.agentArn =      storyAgent.agentCoreRuntime.arn;
    new CfnOutput(this, 'StoryAgentArn', {      value: storyAgent.agentCoreRuntime.arn,    });    new CfnOutput(this, 'InventoryMcpArn', {      value: mcpServer.agentCoreRuntime.arn,    });
    // Grant the agent permissions to invoke our mcp server    mcpServer.agentCoreRuntime.grantInvoke(storyAgent.agentCoreRuntime);
    // Grant the authenticated role access to invoke the api    gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
    // Ensure this is instantiated last so our runtime-config.json can be automatically configured    new GameUI(this, 'GameUI');  }}Tâche 7 : Construire le code
Section intitulée « Tâche 7 : Construire le code »Commandes Nx
Cibles uniques vs multiples
Section intitulée « Cibles uniques vs multiples »La commande run-many exécutera une cible sur plusieurs sous-projets listés (--all les ciblera tous). Cela garantit que les dépendances sont exécutées dans le bon ordre.
Vous pouvez également déclencher un build (ou toute autre tâche) pour une cible de projet unique en exécutant la cible directement sur le projet. Par exemple, pour construire le projet @dungeon-adventure/infra, exécutez la commande suivante :
pnpm nx run @dungeon-adventure/infra:buildyarn nx run @dungeon-adventure/infra:buildnpx nx run @dungeon-adventure/infra:buildbunx nx run @dungeon-adventure/infra:buildVous pouvez également omettre la portée et utiliser la syntaxe abrégée Nx si vous préférez :
pnpm nx build infrayarn nx build infranpx nx build infrabunx nx build infraVisualiser vos dépendances
Section intitulée « Visualiser vos dépendances »Pour visualiser vos dépendances, exécutez :
pnpm nx graphyarn nx graphnpx nx graphbunx nx graph
Mise en cache
Section intitulée « Mise en cache »Nx s’appuie sur la mise en cache pour que vous puissiez réutiliser les artefacts de builds précédents afin d’accélérer le développement. Il y a une certaine configuration requise pour que cela fonctionne correctement et il peut y avoir des cas où vous souhaitez effectuer un build sans utiliser le cache. Pour ce faire, ajoutez simplement l’argument --skip-nx-cache à votre commande. Par exemple :
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cacheyarn nx run @dungeon-adventure/infra:build --skip-nx-cachenpx nx run @dungeon-adventure/infra:build --skip-nx-cachebunx nx run @dungeon-adventure/infra:build --skip-nx-cacheSi pour une raison quelconque vous souhaitez vider votre cache (stocké dans le dossier .nx), vous pouvez exécuter la commande suivante :
pnpm nx resetyarn nx resetnpx nx resetbunx nx resetEn utilisant la ligne de commande, exécutez :
pnpm nx run-many --target build --allyarn nx run-many --target build --allnpx nx run-many --target build --allbunx nx run-many --target build --allVous serez invité avec le message suivant :
 NX   The workspace is out of sync
[@nx/js:typescript-sync]: Certains fichiers de configuration TypeScript manquent de références de projet aux projets dont ils dépendent ou contiennent des références de projet obsolètes.
Cela entraînera une erreur en CI.
? Souhaitez-vous synchroniser les changements identifiés pour mettre à jour votre workspace ? …Oui, synchroniser les changements et exécuter les tâchesNon, exécuter les tâches sans synchroniser les changementsCe message indique que NX a détecté certains fichiers qui peuvent être mis à jour automatiquement pour vous. Dans ce cas, il fait référence aux fichiers tsconfig.json qui n’ont pas de références TypeScript configurées sur les projets référencés.
Sélectionnez l’option Oui, synchroniser les changements et exécuter les tâches pour continuer. Vous devriez remarquer que toutes vos erreurs d’importation liées à l’IDE sont automatiquement résolues car le générateur de synchronisation ajoutera automatiquement les références TypeScript manquantes !
Tous les artefacts construits sont maintenant disponibles dans le dossier dist/ situé à la racine du monorepo. C’est une pratique standard lors de l’utilisation de projets générés par le @aws/nx-plugin car cela ne pollue pas votre arborescence de fichiers avec des fichiers générés. Si vous souhaitez nettoyer vos fichiers, supprimez le dossier dist/ sans vous soucier des fichiers générés dispersés dans l’arborescence de fichiers.
Félicitations ! Vous avez créé tous les sous-projets requis pour commencer à implémenter le cœur de notre jeu d’aventure de donjon IA. 🎉🎉🎉