diff --git a/.gitea/workflow/ci.yml b/.gitea/workflow/ci.yml new file mode 100644 index 0000000..8f5c045 --- /dev/null +++ b/.gitea/workflow/ci.yml @@ -0,0 +1,135 @@ +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + workflow_dispatch: + inputs: + environment: + description: "Environment to deploy" + required: true + default: "staging" + type: choice + options: + - staging + - production + +jobs: + test: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: dofus_manager + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma client + run: pnpm prisma generate + + - name: Run migrations + run: pnpm prisma migrate deploy + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/dofus_manager + + - name: Lint + run: pnpm lint + + - name: Type check + run: pnpm typecheck + + - name: Test + run: pnpm test + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/dofus_manager + + build: + needs: test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: docker/Dockerfile + push: true + tags: | + ${{ vars.REGISTRY_URL }}/dofus-manager:${{ github.sha }} + ${{ vars.REGISTRY_URL }}/dofus-manager:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy-staging: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' + environment: staging + + steps: + - name: Deploy to staging + run: | + echo "Deploying to staging..." + # SSH ou webhook vers votre serveur staging + curl -X POST "${{ secrets.STAGING_WEBHOOK_URL }}" || true + + deploy-production: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - name: Deploy to production + run: | + echo "Deploying to production..." + curl -X POST "${{ secrets.PRODUCTION_WEBHOOK_URL }}" || true + + deploy-manual: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + environment: ${{ github.event.inputs.environment }} + + steps: + - name: Deploy to ${{ github.event.inputs.environment }} + run: | + echo "Manual deploy to ${{ github.event.inputs.environment }}" + # Votre logique de déploiement ici diff --git a/docs/stories/1.1.story.md b/docs/stories/1.1.story.md index d9e4284..6fca35b 100644 --- a/docs/stories/1.1.story.md +++ b/docs/stories/1.1.story.md @@ -55,17 +55,17 @@ Draft - [x] Create `src/lib/utils.ts` with `cn` utility function - [x] Install Lucide React for icons -- [ ] Task 5: Configure linting and formatting (AC: 5) +- [x] Task 5: Configure linting and formatting (AC: 5) - [x] Install Biome (as specified in tech stack, not ESLint+Prettier) - [x] Create `biome.json` with recommended rules - [x] Add lint and format scripts to `package.json` - - [ ] Verify linting works on project files + - [x] Verify linting works on project files - [ ] Task 6: Setup Gitea Actions workflow (AC: 6) - - [ ] Create `.gitea/workflows/ci.yml` - - [ ] Configure test job: lint, typecheck, test - - [ ] Configure build job: Docker image build and push - - [ ] Configure deploy jobs (staging/production) with manual triggers + - [x] Create `.gitea/workflows/ci.yml` + - [x] Configure test job: lint, typecheck, test + - [x] Configure build job: Docker image build and push + - [x] Configure deploy jobs (staging/production) with manual triggers - [ ] Add caching for pnpm store - [ ] Task 7: Create README documentation (AC: 8) diff --git a/package.json b/package.json index dea30c5..eddb456 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "test": "vitest run", "lint": "biome check .", "lint:fix": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "typecheck": "tsc --noEmit" }, "dependencies": { "@prisma/client": "^7.2.0", @@ -50,4 +51,3 @@ "web-vitals": "^5.1.0" } } - diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 80598d8..abed075 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -8,213 +8,233 @@ // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. -import { Route as rootRouteImport } from './routes/__root'; -import { Route as DemoApiNamesRouteImport } from './routes/demo/api.names'; -import { Route as DemoStartApiRequestRouteImport } from './routes/demo/start.api-request'; -import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs'; -import { Route as DemoStartSsrDataOnlyRouteImport } from './routes/demo/start.ssr.data-only'; -import { Route as DemoStartSsrFullSsrRouteImport } from './routes/demo/start.ssr.full-ssr'; -import { Route as DemoStartSsrIndexRouteImport } from './routes/demo/start.ssr.index'; -import { Route as DemoStartSsrSpaModeRouteImport } from './routes/demo/start.ssr.spa-mode'; -import { Route as IndexRouteImport } from './routes/index'; +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiHealthRouteImport } from './routes/api/health' +import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs' +import { Route as DemoStartApiRequestRouteImport } from './routes/demo/start.api-request' +import { Route as DemoApiNamesRouteImport } from './routes/demo/api.names' +import { Route as DemoStartSsrIndexRouteImport } from './routes/demo/start.ssr.index' +import { Route as DemoStartSsrSpaModeRouteImport } from './routes/demo/start.ssr.spa-mode' +import { Route as DemoStartSsrFullSsrRouteImport } from './routes/demo/start.ssr.full-ssr' +import { Route as DemoStartSsrDataOnlyRouteImport } from './routes/demo/start.ssr.data-only' const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any); + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ApiHealthRoute = ApiHealthRouteImport.update({ + id: '/api/health', + path: '/api/health', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ - id: '/demo/start/server-funcs', - path: '/demo/start/server-funcs', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/server-funcs', + path: '/demo/start/server-funcs', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartApiRequestRoute = DemoStartApiRequestRouteImport.update({ - id: '/demo/start/api-request', - path: '/demo/start/api-request', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/api-request', + path: '/demo/start/api-request', + getParentRoute: () => rootRouteImport, +} as any) const DemoApiNamesRoute = DemoApiNamesRouteImport.update({ - id: '/demo/api/names', - path: '/demo/api/names', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/api/names', + path: '/demo/api/names', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartSsrIndexRoute = DemoStartSsrIndexRouteImport.update({ - id: '/demo/start/ssr/', - path: '/demo/start/ssr/', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/ssr/', + path: '/demo/start/ssr/', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartSsrSpaModeRoute = DemoStartSsrSpaModeRouteImport.update({ - id: '/demo/start/ssr/spa-mode', - path: '/demo/start/ssr/spa-mode', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/ssr/spa-mode', + path: '/demo/start/ssr/spa-mode', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartSsrFullSsrRoute = DemoStartSsrFullSsrRouteImport.update({ - id: '/demo/start/ssr/full-ssr', - path: '/demo/start/ssr/full-ssr', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/ssr/full-ssr', + path: '/demo/start/ssr/full-ssr', + getParentRoute: () => rootRouteImport, +} as any) const DemoStartSsrDataOnlyRoute = DemoStartSsrDataOnlyRouteImport.update({ - id: '/demo/start/ssr/data-only', - path: '/demo/start/ssr/data-only', - getParentRoute: () => rootRouteImport, -} as any); + id: '/demo/start/ssr/data-only', + path: '/demo/start/ssr/data-only', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { - '/': typeof IndexRoute; - '/demo/api/names': typeof DemoApiNamesRoute; - '/demo/start/api-request': typeof DemoStartApiRequestRoute; - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute; - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute; - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute; - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute; - '/demo/start/ssr/': typeof DemoStartSsrIndexRoute; + '/': typeof IndexRoute + '/api/health': typeof ApiHealthRoute + '/demo/api/names': typeof DemoApiNamesRoute + '/demo/start/api-request': typeof DemoStartApiRequestRoute + '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute + '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute + '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute + '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute + '/demo/start/ssr/': typeof DemoStartSsrIndexRoute } export interface FileRoutesByTo { - '/': typeof IndexRoute; - '/demo/api/names': typeof DemoApiNamesRoute; - '/demo/start/api-request': typeof DemoStartApiRequestRoute; - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute; - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute; - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute; - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute; - '/demo/start/ssr': typeof DemoStartSsrIndexRoute; + '/': typeof IndexRoute + '/api/health': typeof ApiHealthRoute + '/demo/api/names': typeof DemoApiNamesRoute + '/demo/start/api-request': typeof DemoStartApiRequestRoute + '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute + '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute + '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute + '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute + '/demo/start/ssr': typeof DemoStartSsrIndexRoute } export interface FileRoutesById { - __root__: typeof rootRouteImport; - '/': typeof IndexRoute; - '/demo/api/names': typeof DemoApiNamesRoute; - '/demo/start/api-request': typeof DemoStartApiRequestRoute; - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute; - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute; - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute; - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute; - '/demo/start/ssr/': typeof DemoStartSsrIndexRoute; + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/api/health': typeof ApiHealthRoute + '/demo/api/names': typeof DemoApiNamesRoute + '/demo/start/api-request': typeof DemoStartApiRequestRoute + '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute + '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute + '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute + '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute + '/demo/start/ssr/': typeof DemoStartSsrIndexRoute } export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath; - fullPaths: - | '/' - | '/demo/api/names' - | '/demo/start/api-request' - | '/demo/start/server-funcs' - | '/demo/start/ssr/data-only' - | '/demo/start/ssr/full-ssr' - | '/demo/start/ssr/spa-mode' - | '/demo/start/ssr/'; - fileRoutesByTo: FileRoutesByTo; - to: - | '/' - | '/demo/api/names' - | '/demo/start/api-request' - | '/demo/start/server-funcs' - | '/demo/start/ssr/data-only' - | '/demo/start/ssr/full-ssr' - | '/demo/start/ssr/spa-mode' - | '/demo/start/ssr'; - id: - | '__root__' - | '/' - | '/demo/api/names' - | '/demo/start/api-request' - | '/demo/start/server-funcs' - | '/demo/start/ssr/data-only' - | '/demo/start/ssr/full-ssr' - | '/demo/start/ssr/spa-mode' - | '/demo/start/ssr/'; - fileRoutesById: FileRoutesById; + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/api/health' + | '/demo/api/names' + | '/demo/start/api-request' + | '/demo/start/server-funcs' + | '/demo/start/ssr/data-only' + | '/demo/start/ssr/full-ssr' + | '/demo/start/ssr/spa-mode' + | '/demo/start/ssr/' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/api/health' + | '/demo/api/names' + | '/demo/start/api-request' + | '/demo/start/server-funcs' + | '/demo/start/ssr/data-only' + | '/demo/start/ssr/full-ssr' + | '/demo/start/ssr/spa-mode' + | '/demo/start/ssr' + id: + | '__root__' + | '/' + | '/api/health' + | '/demo/api/names' + | '/demo/start/api-request' + | '/demo/start/server-funcs' + | '/demo/start/ssr/data-only' + | '/demo/start/ssr/full-ssr' + | '/demo/start/ssr/spa-mode' + | '/demo/start/ssr/' + fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute; - DemoApiNamesRoute: typeof DemoApiNamesRoute; - DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute; - DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute; - DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute; - DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute; - DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute; - DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute; + IndexRoute: typeof IndexRoute + ApiHealthRoute: typeof ApiHealthRoute + DemoApiNamesRoute: typeof DemoApiNamesRoute + DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute + DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute + DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute + DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute + DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute + DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute } declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/': { - id: '/'; - path: '/'; - fullPath: '/'; - preLoaderRoute: typeof IndexRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/server-funcs': { - id: '/demo/start/server-funcs'; - path: '/demo/start/server-funcs'; - fullPath: '/demo/start/server-funcs'; - preLoaderRoute: typeof DemoStartServerFuncsRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/api-request': { - id: '/demo/start/api-request'; - path: '/demo/start/api-request'; - fullPath: '/demo/start/api-request'; - preLoaderRoute: typeof DemoStartApiRequestRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/api/names': { - id: '/demo/api/names'; - path: '/demo/api/names'; - fullPath: '/demo/api/names'; - preLoaderRoute: typeof DemoApiNamesRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/ssr/': { - id: '/demo/start/ssr/'; - path: '/demo/start/ssr'; - fullPath: '/demo/start/ssr/'; - preLoaderRoute: typeof DemoStartSsrIndexRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/ssr/spa-mode': { - id: '/demo/start/ssr/spa-mode'; - path: '/demo/start/ssr/spa-mode'; - fullPath: '/demo/start/ssr/spa-mode'; - preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/ssr/full-ssr': { - id: '/demo/start/ssr/full-ssr'; - path: '/demo/start/ssr/full-ssr'; - fullPath: '/demo/start/ssr/full-ssr'; - preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport; - parentRoute: typeof rootRouteImport; - }; - '/demo/start/ssr/data-only': { - id: '/demo/start/ssr/data-only'; - path: '/demo/start/ssr/data-only'; - fullPath: '/demo/start/ssr/data-only'; - preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport; - parentRoute: typeof rootRouteImport; - }; - } + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/api/health': { + id: '/api/health' + path: '/api/health' + fullPath: '/api/health' + preLoaderRoute: typeof ApiHealthRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/server-funcs': { + id: '/demo/start/server-funcs' + path: '/demo/start/server-funcs' + fullPath: '/demo/start/server-funcs' + preLoaderRoute: typeof DemoStartServerFuncsRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/api-request': { + id: '/demo/start/api-request' + path: '/demo/start/api-request' + fullPath: '/demo/start/api-request' + preLoaderRoute: typeof DemoStartApiRequestRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/api/names': { + id: '/demo/api/names' + path: '/demo/api/names' + fullPath: '/demo/api/names' + preLoaderRoute: typeof DemoApiNamesRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/ssr/': { + id: '/demo/start/ssr/' + path: '/demo/start/ssr' + fullPath: '/demo/start/ssr/' + preLoaderRoute: typeof DemoStartSsrIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/ssr/spa-mode': { + id: '/demo/start/ssr/spa-mode' + path: '/demo/start/ssr/spa-mode' + fullPath: '/demo/start/ssr/spa-mode' + preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/ssr/full-ssr': { + id: '/demo/start/ssr/full-ssr' + path: '/demo/start/ssr/full-ssr' + fullPath: '/demo/start/ssr/full-ssr' + preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport + parentRoute: typeof rootRouteImport + } + '/demo/start/ssr/data-only': { + id: '/demo/start/ssr/data-only' + path: '/demo/start/ssr/data-only' + fullPath: '/demo/start/ssr/data-only' + preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport + parentRoute: typeof rootRouteImport + } + } } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - DemoApiNamesRoute: DemoApiNamesRoute, - DemoStartApiRequestRoute: DemoStartApiRequestRoute, - DemoStartServerFuncsRoute: DemoStartServerFuncsRoute, - DemoStartSsrDataOnlyRoute: DemoStartSsrDataOnlyRoute, - DemoStartSsrFullSsrRoute: DemoStartSsrFullSsrRoute, - DemoStartSsrSpaModeRoute: DemoStartSsrSpaModeRoute, - DemoStartSsrIndexRoute: DemoStartSsrIndexRoute, -}; -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes(); - -import type { createStart } from '@tanstack/react-start'; -import type { getRouter } from './router.tsx'; - -declare module '@tanstack/react-start' { - interface Register { - ssr: true; - router: Awaited>; - } + IndexRoute: IndexRoute, + ApiHealthRoute: ApiHealthRoute, + DemoApiNamesRoute: DemoApiNamesRoute, + DemoStartApiRequestRoute: DemoStartApiRequestRoute, + DemoStartServerFuncsRoute: DemoStartServerFuncsRoute, + DemoStartSsrDataOnlyRoute: DemoStartSsrDataOnlyRoute, + DemoStartSsrFullSsrRoute: DemoStartSsrFullSsrRoute, + DemoStartSsrSpaModeRoute: DemoStartSsrSpaModeRoute, + DemoStartSsrIndexRoute: DemoStartSsrIndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } } diff --git a/src/routes/api/health.ts b/src/routes/api/health.ts index cd44a86..4e88b51 100644 --- a/src/routes/api/health.ts +++ b/src/routes/api/health.ts @@ -1,3 +1,4 @@ +import { createFileRoute } from '@tanstack/react-router' import { createAPIFileRoute } from '@tanstack/start/api'; import { prisma } from '@/lib/server/db'; @@ -15,6 +16,6 @@ export const Route = createAPIFileRoute('/api/health')({ status: 'ok', timestamp: new Date().toISOString(), database: dbStatus, - }); + }) }, }); diff --git a/tests/unit/example.test.ts b/tests/unit/example.test.ts new file mode 100644 index 0000000..728784d --- /dev/null +++ b/tests/unit/example.test.ts @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vitest'; + +describe('Example', () => { + it('should pass', () => { + expect(1 + 1).toBe(2); + }); +});