feat(#1): Set up monorepo scaffold with pnpm workspaces + TurboRepo
Implements the foundational project structure including: - pnpm workspaces configuration - TurboRepo for build orchestration - NestJS 11.1.12 API (apps/api) - Next.js 16.1.6 web app (apps/web) - Shared packages (config, shared, ui) - TypeScript strict mode configuration - ESLint + Prettier setup - Vitest for unit testing (19 passing tests) Fixes #1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
17
.env.example
Normal file
17
.env.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# API Configuration
|
||||||
|
API_PORT=3001
|
||||||
|
API_HOST=0.0.0.0
|
||||||
|
|
||||||
|
# Web Configuration
|
||||||
|
NEXT_PUBLIC_API_URL=http://localhost:3001
|
||||||
|
|
||||||
|
# Database (configured in later milestone)
|
||||||
|
# DATABASE_URL=postgresql://user:password@localhost:5432/mosaic
|
||||||
|
|
||||||
|
# Authentication (configured in later milestone)
|
||||||
|
# OIDC_ISSUER=https://auth.example.com
|
||||||
|
# OIDC_CLIENT_ID=your-client-id
|
||||||
|
# OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
|
||||||
|
# Development
|
||||||
|
NODE_ENV=development
|
||||||
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
.pnpm-store
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist
|
||||||
|
.next
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.tsbuildinfo
|
||||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.next
|
||||||
|
.turbo
|
||||||
|
coverage
|
||||||
|
pnpm-lock.yaml
|
||||||
3
.prettierrc.js
Normal file
3
.prettierrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import config from "@mosaic/config/prettier";
|
||||||
|
|
||||||
|
export default config;
|
||||||
313
CLAUDE.md
Normal file
313
CLAUDE.md
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
**Multi-tenant personal assistant platform with PostgreSQL backend, Authentik SSO, and MoltBot
|
||||||
|
integration.**
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Mosaic Stack is a standalone platform that provides:
|
||||||
|
- Multi-user workspaces with team sharing
|
||||||
|
- Task, event, and project management
|
||||||
|
- Gantt charts and Kanban boards
|
||||||
|
- MoltBot integration via plugins (stock MoltBot + mosaic-plugin-*)
|
||||||
|
- PDA-friendly design throughout
|
||||||
|
|
||||||
|
**Repository:** git.mosaicstack.dev/mosaic/stack
|
||||||
|
**Versioning:** Start at 0.0.1, MVP = 0.1.0
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|-------|------------|
|
||||||
|
| Frontend | Next.js 16 + React + TailwindCSS + Shadcn/ui |
|
||||||
|
| Backend | NestJS + Prisma ORM |
|
||||||
|
| Database | PostgreSQL 17 + pgvector |
|
||||||
|
| Cache | Valkey (Redis-compatible) |
|
||||||
|
| Auth | Authentik (OIDC) |
|
||||||
|
| AI | Ollama (configurable: local or remote) |
|
||||||
|
| Messaging | MoltBot (stock + Mosaic plugins) |
|
||||||
|
| Real-time | WebSockets (Socket.io) |
|
||||||
|
| Monorepo | pnpm workspaces + TurboRepo |
|
||||||
|
| Testing | Vitest + Playwright |
|
||||||
|
| Deployment | Docker + docker-compose |
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
mosaic-stack/
|
||||||
|
├── apps/
|
||||||
|
│ ├── api/ # mosaic-api (NestJS)
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── auth/ # Authentik OIDC
|
||||||
|
│ │ │ ├── tasks/ # Task management
|
||||||
|
│ │ │ ├── events/ # Calendar/events
|
||||||
|
│ │ │ ├── projects/ # Project management
|
||||||
|
│ │ │ ├── brain/ # MoltBot integration
|
||||||
|
│ │ │ └── activity/ # Activity logging
|
||||||
|
│ │ ├── prisma/
|
||||||
|
│ │ │ └── schema.prisma
|
||||||
|
│ │ └── Dockerfile
|
||||||
|
│ └── web/ # mosaic-web (Next.js 16)
|
||||||
|
│ ├── app/
|
||||||
|
│ ├── components/
|
||||||
|
│ └── Dockerfile
|
||||||
|
├── packages/
|
||||||
|
│ ├── shared/ # Shared types, utilities
|
||||||
|
│ ├── ui/ # Shared UI components
|
||||||
|
│ └── config/ # Shared configuration
|
||||||
|
├── plugins/
|
||||||
|
│ ├── mosaic-plugin-brain/ # MoltBot skill: API queries
|
||||||
|
│ ├── mosaic-plugin-calendar/ # MoltBot skill: Calendar
|
||||||
|
│ ├── mosaic-plugin-tasks/ # MoltBot skill: Tasks
|
||||||
|
│ └── mosaic-plugin-gantt/ # MoltBot skill: Gantt
|
||||||
|
├── docker/
|
||||||
|
│ ├── docker-compose.yml # Turnkey deployment
|
||||||
|
│ └── init-scripts/ # PostgreSQL init
|
||||||
|
├── docs/
|
||||||
|
│ ├── SETUP.md
|
||||||
|
│ ├── CONFIGURATION.md
|
||||||
|
│ └── DESIGN-PRINCIPLES.md
|
||||||
|
├── .env.example
|
||||||
|
├── turbo.json
|
||||||
|
├── pnpm-workspace.yaml
|
||||||
|
└── README.md
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Branch Strategy
|
||||||
|
- `main` — stable releases only
|
||||||
|
- `develop` — active development (default working branch)
|
||||||
|
- `feature/*` — feature branches from develop
|
||||||
|
- `fix/*` — bug fix branches
|
||||||
|
|
||||||
|
### Starting Work
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git pull --rebase
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
Running Locally
|
||||||
|
|
||||||
|
# Start all services (Docker)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Or run individually for development
|
||||||
|
pnpm dev # All apps
|
||||||
|
pnpm dev:api # API only
|
||||||
|
pnpm dev:web # Web only
|
||||||
|
|
||||||
|
Testing
|
||||||
|
|
||||||
|
pnpm test # Run all tests
|
||||||
|
pnpm test:api # API tests only
|
||||||
|
pnpm test:web # Web tests only
|
||||||
|
pnpm test:e2e # Playwright E2E
|
||||||
|
|
||||||
|
Building
|
||||||
|
|
||||||
|
pnpm build # Build all
|
||||||
|
pnpm build:api # Build API
|
||||||
|
pnpm build:web # Build Web
|
||||||
|
|
||||||
|
Design Principles (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
PDA-Friendly Language
|
||||||
|
|
||||||
|
NEVER use demanding language. This is critical.
|
||||||
|
┌─────────────┬──────────────────────┐
|
||||||
|
│ ❌ NEVER │ ✅ ALWAYS │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ OVERDUE │ Target passed │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ URGENT │ Approaching target │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ MUST DO │ Scheduled for │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ CRITICAL │ High priority │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ YOU NEED TO │ Consider / Option to │
|
||||||
|
├─────────────┼──────────────────────┤
|
||||||
|
│ REQUIRED │ Recommended │
|
||||||
|
└─────────────┴──────────────────────┘
|
||||||
|
Visual Indicators
|
||||||
|
|
||||||
|
Use status indicators consistently:
|
||||||
|
- 🟢 On track / Active
|
||||||
|
- 🔵 Upcoming / Scheduled
|
||||||
|
- ⏸️ Paused / On hold
|
||||||
|
- 💤 Dormant / Inactive
|
||||||
|
- ⚪ Not started
|
||||||
|
|
||||||
|
Display Principles
|
||||||
|
|
||||||
|
1. 10-second scannability — Key info visible immediately
|
||||||
|
2. Visual chunking — Clear sections with headers
|
||||||
|
3. Single-line items — Compact, scannable lists
|
||||||
|
4. Date grouping — Today, Tomorrow, This Week headers
|
||||||
|
5. Progressive disclosure — Details on click, not upfront
|
||||||
|
6. Calm colors — No aggressive reds for status
|
||||||
|
|
||||||
|
Reference
|
||||||
|
|
||||||
|
See docs/DESIGN-PRINCIPLES.md for complete guidelines.
|
||||||
|
For original patterns, see: jarvis-brain/docs/DESIGN-PRINCIPLES.md
|
||||||
|
|
||||||
|
API Conventions
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
|
||||||
|
GET /api/{resource} # List (with pagination, filters)
|
||||||
|
GET /api/{resource}/:id # Get single
|
||||||
|
POST /api/{resource} # Create
|
||||||
|
PATCH /api/{resource}/:id # Update
|
||||||
|
DELETE /api/{resource}/:id # Delete
|
||||||
|
|
||||||
|
Response Format
|
||||||
|
|
||||||
|
// Success
|
||||||
|
{
|
||||||
|
data: T | T[],
|
||||||
|
meta?: { total, page, limit }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error
|
||||||
|
{
|
||||||
|
error: {
|
||||||
|
code: string,
|
||||||
|
message: string,
|
||||||
|
details?: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Brain Query API
|
||||||
|
|
||||||
|
POST /api/brain/query
|
||||||
|
{
|
||||||
|
query: "what's on my calendar",
|
||||||
|
context?: { view: "dashboard", workspace_id: "..." }
|
||||||
|
}
|
||||||
|
|
||||||
|
Database Conventions
|
||||||
|
|
||||||
|
Multi-Tenant (RLS)
|
||||||
|
|
||||||
|
All workspace-scoped tables use Row-Level Security:
|
||||||
|
- Always include workspace_id in queries
|
||||||
|
- RLS policies enforce isolation
|
||||||
|
- Set session context for current user
|
||||||
|
|
||||||
|
Prisma Commands
|
||||||
|
|
||||||
|
pnpm prisma:generate # Generate client
|
||||||
|
pnpm prisma:migrate # Run migrations
|
||||||
|
pnpm prisma:studio # Open Prisma Studio
|
||||||
|
pnpm prisma:seed # Seed development data
|
||||||
|
|
||||||
|
MoltBot Plugin Development
|
||||||
|
|
||||||
|
Plugins live in plugins/mosaic-plugin-*/ and follow MoltBot skill format:
|
||||||
|
|
||||||
|
# plugins/mosaic-plugin-brain/SKILL.md
|
||||||
|
---
|
||||||
|
name: mosaic-plugin-brain
|
||||||
|
description: Query Mosaic Stack for tasks, events, projects
|
||||||
|
version: 0.0.1
|
||||||
|
triggers:
|
||||||
|
- "what's on my calendar"
|
||||||
|
- "show my tasks"
|
||||||
|
- "morning briefing"
|
||||||
|
tools:
|
||||||
|
- mosaic_api
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin instructions here...
|
||||||
|
|
||||||
|
Key principle: MoltBot remains stock. All customization via plugins only.
|
||||||
|
|
||||||
|
Environment Variables
|
||||||
|
|
||||||
|
See .env.example for all variables. Key ones:
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=postgresql://mosaic:password@localhost:5432/mosaic
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
AUTHENTIK_URL=https://auth.example.com
|
||||||
|
AUTHENTIK_CLIENT_ID=mosaic-stack
|
||||||
|
AUTHENTIK_CLIENT_SECRET=...
|
||||||
|
|
||||||
|
# Ollama
|
||||||
|
OLLAMA_MODE=local|remote
|
||||||
|
OLLAMA_ENDPOINT=http://localhost:11434
|
||||||
|
|
||||||
|
# MoltBot
|
||||||
|
MOSAIC_API_TOKEN=...
|
||||||
|
|
||||||
|
Issue Tracking
|
||||||
|
|
||||||
|
Issues are tracked at: https://git.mosaicstack.dev/mosaic/stack/issues
|
||||||
|
|
||||||
|
Labels
|
||||||
|
|
||||||
|
- Priority: p0 (critical), p1 (high), p2 (medium), p3 (low)
|
||||||
|
- Type: api, web, database, auth, plugin, ai, devops, docs, migration, security, testing,
|
||||||
|
performance, setup
|
||||||
|
|
||||||
|
Milestones
|
||||||
|
|
||||||
|
- M1-Foundation (0.0.x)
|
||||||
|
- M2-MultiTenant (0.0.x)
|
||||||
|
- M3-Features (0.0.x)
|
||||||
|
- M4-MoltBot (0.0.x)
|
||||||
|
- M5-Migration (0.1.0 MVP)
|
||||||
|
|
||||||
|
Commit Format
|
||||||
|
|
||||||
|
<type>(#issue): Brief description
|
||||||
|
|
||||||
|
Detailed explanation if needed.
|
||||||
|
|
||||||
|
Fixes #123
|
||||||
|
Types: feat, fix, docs, test, refactor, chore
|
||||||
|
|
||||||
|
Testing Requirements
|
||||||
|
|
||||||
|
- Minimum 85% coverage for new code
|
||||||
|
- Write tests before implementation (TDD)
|
||||||
|
- All tests must pass before PR merge
|
||||||
|
|
||||||
|
Docker Deployment
|
||||||
|
|
||||||
|
Turnkey (includes everything)
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
Customized (external services)
|
||||||
|
|
||||||
|
Create docker-compose.override.yml to:
|
||||||
|
- Point to external PostgreSQL/Valkey/Ollama
|
||||||
|
- Disable bundled services
|
||||||
|
|
||||||
|
See docs/DOCKER.md for details.
|
||||||
|
|
||||||
|
Key Documentation
|
||||||
|
┌───────────────────────────┬───────────────────────┐
|
||||||
|
│ Document │ Purpose │
|
||||||
|
├───────────────────────────┼───────────────────────┤
|
||||||
|
│ docs/SETUP.md │ Installation guide │
|
||||||
|
├───────────────────────────┼───────────────────────┤
|
||||||
|
│ docs/CONFIGURATION.md │ All config options │
|
||||||
|
├───────────────────────────┼───────────────────────┤
|
||||||
|
│ docs/DESIGN-PRINCIPLES.md │ PDA-friendly patterns │
|
||||||
|
├───────────────────────────┼───────────────────────┤
|
||||||
|
│ docs/DOCKER.md │ Docker deployment │
|
||||||
|
├───────────────────────────┼───────────────────────┤
|
||||||
|
│ docs/API.md │ API documentation │
|
||||||
|
└───────────────────────────┴───────────────────────┘
|
||||||
|
Related Repositories
|
||||||
|
┌──────────────┬──────────────────────────────────────────────┐
|
||||||
|
│ Repo │ Purpose │
|
||||||
|
├──────────────┼──────────────────────────────────────────────┤
|
||||||
|
│ jarvis-brain │ Original JSON-based brain (migration source) │
|
||||||
|
├──────────────┼──────────────────────────────────────────────┤
|
||||||
|
│ MoltBot │ Stock messaging gateway │
|
||||||
|
└──────────────┴──────────────────────────────────────────────┘
|
||||||
|
---
|
||||||
|
Mosaic Stack v0.0.x — Building the future of personal assistants.
|
||||||
18
apps/api/.swcrc
Normal file
18
apps/api/.swcrc
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"sourceMaps": true,
|
||||||
|
"jsc": {
|
||||||
|
"target": "es2022",
|
||||||
|
"parser": {
|
||||||
|
"syntax": "typescript",
|
||||||
|
"decorators": true
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"legacyDecorator": true,
|
||||||
|
"decoratorMetadata": true
|
||||||
|
},
|
||||||
|
"keepClassNames": true
|
||||||
|
},
|
||||||
|
"module": {
|
||||||
|
"type": "es6"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apps/api/nest-cli.json
Normal file
8
apps/api/nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
40
apps/api/package.json
Normal file
40
apps/api/package.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@mosaic/api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"dev": "nest start --watch",
|
||||||
|
"start": "node dist/main",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"src/**/*.ts\"",
|
||||||
|
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"test:e2e": "vitest run --config ./vitest.e2e.config.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^11.1.12",
|
||||||
|
"@nestjs/core": "^11.1.12",
|
||||||
|
"@nestjs/platform-express": "^11.1.12",
|
||||||
|
"@mosaic/shared": "workspace:*",
|
||||||
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rxjs": "^7.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mosaic/config": "workspace:*",
|
||||||
|
"@nestjs/cli": "^11.0.6",
|
||||||
|
"@nestjs/schematics": "^11.0.1",
|
||||||
|
"@nestjs/testing": "^11.1.12",
|
||||||
|
"@swc/core": "^1.10.18",
|
||||||
|
"@types/express": "^5.0.1",
|
||||||
|
"@types/node": "^22.13.4",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"unplugin-swc": "^1.5.2",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
apps/api/src/app.controller.d.ts
vendored
Normal file
14
apps/api/src/app.controller.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { AppService } from "./app.service";
|
||||||
|
import type { ApiResponse } from "@mosaic/shared/types";
|
||||||
|
interface HealthStatus {
|
||||||
|
status: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
export declare class AppController {
|
||||||
|
private readonly appService;
|
||||||
|
constructor(appService: AppService);
|
||||||
|
getHello(): string;
|
||||||
|
getHealth(): ApiResponse<HealthStatus>;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=app.controller.d.ts.map
|
||||||
1
apps/api/src/app.controller.d.ts.map
Normal file
1
apps/api/src/app.controller.d.ts.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.controller.d.ts","sourceRoot":"","sources":["app.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBACa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAGnD,QAAQ,IAAI,MAAM;IAKlB,SAAS,IAAI,WAAW,CAAC,YAAY,CAAC;CASvC"}
|
||||||
50
apps/api/src/app.controller.js
Normal file
50
apps/api/src/app.controller.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use strict";
|
||||||
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.AppController = void 0;
|
||||||
|
const common_1 = require("@nestjs/common");
|
||||||
|
const app_service_1 = require("./app.service");
|
||||||
|
let AppController = class AppController {
|
||||||
|
appService;
|
||||||
|
constructor(appService) {
|
||||||
|
this.appService = appService;
|
||||||
|
}
|
||||||
|
getHello() {
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
|
getHealth() {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
status: "healthy",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.AppController = AppController;
|
||||||
|
__decorate([
|
||||||
|
(0, common_1.Get)(),
|
||||||
|
__metadata("design:type", Function),
|
||||||
|
__metadata("design:paramtypes", []),
|
||||||
|
__metadata("design:returntype", String)
|
||||||
|
], AppController.prototype, "getHello", null);
|
||||||
|
__decorate([
|
||||||
|
(0, common_1.Get)("health"),
|
||||||
|
__metadata("design:type", Function),
|
||||||
|
__metadata("design:paramtypes", []),
|
||||||
|
__metadata("design:returntype", Object)
|
||||||
|
], AppController.prototype, "getHealth", null);
|
||||||
|
exports.AppController = AppController = __decorate([
|
||||||
|
(0, common_1.Controller)(),
|
||||||
|
__metadata("design:paramtypes", [app_service_1.AppService])
|
||||||
|
], AppController);
|
||||||
|
//# sourceMappingURL=app.controller.js.map
|
||||||
1
apps/api/src/app.controller.js.map
Normal file
1
apps/api/src/app.controller.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,+CAA2C;AASpC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACK;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAGvD,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;IAGD,SAAS;QACP,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AAlBY,sCAAa;AAIxB;IADC,IAAA,YAAG,GAAE;;;;6CAGL;AAGD;IADC,IAAA,YAAG,EAAC,QAAQ,CAAC;;;;8CASb;wBAjBU,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAE8B,wBAAU;GADxC,aAAa,CAkBzB"}
|
||||||
31
apps/api/src/app.controller.test.ts
Normal file
31
apps/api/src/app.controller.test.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { AppService } from "./app.service";
|
||||||
|
import { AppController } from "./app.controller";
|
||||||
|
|
||||||
|
describe("AppController", () => {
|
||||||
|
const appService = new AppService();
|
||||||
|
const controller = new AppController(appService);
|
||||||
|
|
||||||
|
describe("getHello", () => {
|
||||||
|
it('should return "Mosaic Stack API"', () => {
|
||||||
|
expect(controller.getHello()).toBe("Mosaic Stack API");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getHealth", () => {
|
||||||
|
it("should return health status", () => {
|
||||||
|
const result = controller.getHealth();
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.data.status).toBe("healthy");
|
||||||
|
expect(result.data.timestamp).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AppService", () => {
|
||||||
|
const service = new AppService();
|
||||||
|
|
||||||
|
it('should return "Mosaic Stack API"', () => {
|
||||||
|
expect(service.getHello()).toBe("Mosaic Stack API");
|
||||||
|
});
|
||||||
|
});
|
||||||
29
apps/api/src/app.controller.ts
Normal file
29
apps/api/src/app.controller.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Controller, Get } from "@nestjs/common";
|
||||||
|
import { AppService } from "./app.service";
|
||||||
|
import type { ApiResponse } from "@mosaic/shared";
|
||||||
|
|
||||||
|
interface HealthStatus {
|
||||||
|
status: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello(): string {
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("health")
|
||||||
|
getHealth(): ApiResponse<HealthStatus> {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
status: "healthy",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
3
apps/api/src/app.module.d.ts
vendored
Normal file
3
apps/api/src/app.module.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare class AppModule {
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=app.module.d.ts.map
|
||||||
1
apps/api/src/app.module.d.ts.map
Normal file
1
apps/api/src/app.module.d.ts.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.module.d.ts","sourceRoot":"","sources":["app.module.ts"],"names":[],"mappings":"AAIA,qBAKa,SAAS;CAAG"}
|
||||||
23
apps/api/src/app.module.js
Normal file
23
apps/api/src/app.module.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"use strict";
|
||||||
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.AppModule = void 0;
|
||||||
|
const common_1 = require("@nestjs/common");
|
||||||
|
const app_controller_1 = require("./app.controller");
|
||||||
|
const app_service_1 = require("./app.service");
|
||||||
|
let AppModule = class AppModule {
|
||||||
|
};
|
||||||
|
exports.AppModule = AppModule;
|
||||||
|
exports.AppModule = AppModule = __decorate([
|
||||||
|
(0, common_1.Module)({
|
||||||
|
imports: [],
|
||||||
|
controllers: [app_controller_1.AppController],
|
||||||
|
providers: [app_service_1.AppService],
|
||||||
|
})
|
||||||
|
], AppModule);
|
||||||
|
//# sourceMappingURL=app.module.js.map
|
||||||
1
apps/api/src/app.module.js.map
Normal file
1
apps/api/src/app.module.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,qDAAiD;AACjD,+CAA2C;AAOpC,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IALrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC,8BAAa,CAAC;QAC5B,SAAS,EAAE,CAAC,wBAAU,CAAC;KACxB,CAAC;GACW,SAAS,CAAG"}
|
||||||
10
apps/api/src/app.module.ts
Normal file
10
apps/api/src/app.module.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { AppController } from "./app.controller";
|
||||||
|
import { AppService } from "./app.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
4
apps/api/src/app.service.d.ts
vendored
Normal file
4
apps/api/src/app.service.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export declare class AppService {
|
||||||
|
getHello(): string;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=app.service.d.ts.map
|
||||||
1
apps/api/src/app.service.d.ts.map
Normal file
1
apps/api/src/app.service.d.ts.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.service.d.ts","sourceRoot":"","sources":["app.service.ts"],"names":[],"mappings":"AAEA,qBACa,UAAU;IACrB,QAAQ,IAAI,MAAM;CAGnB"}
|
||||||
20
apps/api/src/app.service.js
Normal file
20
apps/api/src/app.service.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"use strict";
|
||||||
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.AppService = void 0;
|
||||||
|
const common_1 = require("@nestjs/common");
|
||||||
|
let AppService = class AppService {
|
||||||
|
getHello() {
|
||||||
|
return "Mosaic Stack API";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.AppService = AppService;
|
||||||
|
exports.AppService = AppService = __decorate([
|
||||||
|
(0, common_1.Injectable)()
|
||||||
|
], AppService);
|
||||||
|
//# sourceMappingURL=app.service.js.map
|
||||||
1
apps/api/src/app.service.js.map
Normal file
1
apps/api/src/app.service.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["app.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,QAAQ;QACN,OAAO,kBAAkB,CAAC;IAC5B,CAAC;CACF,CAAA;AAJY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;GACA,UAAU,CAItB"}
|
||||||
8
apps/api/src/app.service.ts
Normal file
8
apps/api/src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return "Mosaic Stack API";
|
||||||
|
}
|
||||||
|
}
|
||||||
2
apps/api/src/main.d.ts
vendored
Normal file
2
apps/api/src/main.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export {};
|
||||||
|
//# sourceMappingURL=main.d.ts.map
|
||||||
1
apps/api/src/main.d.ts.map
Normal file
1
apps/api/src/main.d.ts.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":""}
|
||||||
16
apps/api/src/main.js
Normal file
16
apps/api/src/main.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const core_1 = require("@nestjs/core");
|
||||||
|
const app_module_1 = require("./app.module");
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await core_1.NestFactory.create(app_module_1.AppModule);
|
||||||
|
app.enableCors();
|
||||||
|
const port = process.env["PORT"] ?? 3001;
|
||||||
|
await app.listen(port);
|
||||||
|
console.log(`API running on http://localhost:${port}`);
|
||||||
|
}
|
||||||
|
bootstrap().catch((err) => {
|
||||||
|
console.error("Failed to start application:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
//# sourceMappingURL=main.js.map
|
||||||
1
apps/api/src/main.js.map
Normal file
1
apps/api/src/main.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,GAAG,CAAC,UAAU,EAAE,CAAC;IAEjB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IACzC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACjC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
||||||
17
apps/api/src/main.ts
Normal file
17
apps/api/src/main.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { NestFactory } from "@nestjs/core";
|
||||||
|
import { AppModule } from "./app.module";
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.enableCors();
|
||||||
|
|
||||||
|
const port = process.env["PORT"] ?? 3001;
|
||||||
|
await app.listen(port);
|
||||||
|
|
||||||
|
console.log(`API running on http://localhost:${port}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap().catch((err: unknown) => {
|
||||||
|
console.error("Failed to start application:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
13
apps/api/tsconfig.json
Normal file
13
apps/api/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "@mosaic/config/typescript/nestjs",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
|
||||||
|
}
|
||||||
27
apps/api/vitest.config.ts
Normal file
27
apps/api/vitest.config.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import swc from "unplugin-swc";
|
||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: false,
|
||||||
|
environment: "node",
|
||||||
|
include: ["src/**/*.test.ts", "src/**/*.spec.ts"],
|
||||||
|
coverage: {
|
||||||
|
provider: "v8",
|
||||||
|
reporter: ["text", "json", "html"],
|
||||||
|
exclude: ["node_modules/", "dist/"],
|
||||||
|
},
|
||||||
|
setupFiles: ["./vitest.setup.ts"],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
swc.vite({
|
||||||
|
module: { type: "es6" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
1
apps/api/vitest.setup.ts
Normal file
1
apps/api/vitest.setup.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import "reflect-metadata";
|
||||||
6
apps/web/next-env.d.ts
vendored
Normal file
6
apps/web/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
7
apps/web/next.config.ts
Normal file
7
apps/web/next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
transpilePackages: ["@mosaic/ui", "@mosaic/shared"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
36
apps/web/package.json
Normal file
36
apps/web/package.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "@mosaic/web",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"dev": "next dev --turbopack --port 3000",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"lint:fix": "next lint --fix",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"clean": "rm -rf .next",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mosaic/shared": "workspace:*",
|
||||||
|
"@mosaic/ui": "workspace:*",
|
||||||
|
"next": "^16.1.6",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mosaic/config": "workspace:*",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.2.0",
|
||||||
|
"@types/node": "^22.13.4",
|
||||||
|
"@types/react": "^19.0.8",
|
||||||
|
"@types/react-dom": "^19.0.3",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"jsdom": "^26.0.0",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
apps/web/src/app/globals.css
Normal file
20
apps/web/src/app/globals.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--foreground-rgb: 0, 0, 0;
|
||||||
|
--background-rgb: 255, 255, 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--foreground-rgb: 255, 255, 255;
|
||||||
|
--background-rgb: 0, 0, 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: rgb(var(--foreground-rgb));
|
||||||
|
background: rgb(var(--background-rgb));
|
||||||
|
}
|
||||||
16
apps/web/src/app/layout.tsx
Normal file
16
apps/web/src/app/layout.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Mosaic Stack",
|
||||||
|
description: "Mosaic Stack Web Application",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
apps/web/src/app/page.test.tsx
Normal file
22
apps/web/src/app/page.test.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { describe, expect, it, afterEach } from "vitest";
|
||||||
|
import { render, screen, cleanup } from "@testing-library/react";
|
||||||
|
import Home from "./page";
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Home", () => {
|
||||||
|
it("should render the title", () => {
|
||||||
|
render(<Home />);
|
||||||
|
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent("Mosaic Stack");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the buttons", () => {
|
||||||
|
render(<Home />);
|
||||||
|
const buttons = screen.getAllByRole("button");
|
||||||
|
expect(buttons.length).toBe(2);
|
||||||
|
expect(buttons[0]).toHaveTextContent("Get Started");
|
||||||
|
expect(buttons[1]).toHaveTextContent("Learn More");
|
||||||
|
});
|
||||||
|
});
|
||||||
16
apps/web/src/app/page.tsx
Normal file
16
apps/web/src/app/page.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Button } from "@mosaic/ui";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
||||||
|
<h1 className="text-4xl font-bold mb-8">Mosaic Stack</h1>
|
||||||
|
<p className="text-lg text-gray-600 mb-8">
|
||||||
|
Welcome to the Mosaic Stack monorepo
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Button variant="primary">Get Started</Button>
|
||||||
|
<Button variant="secondary">Learn More</Button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
apps/web/tsconfig.json
Normal file
11
apps/web/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "@mosaic/config/typescript/nextjs",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
23
apps/web/vitest.config.ts
Normal file
23
apps/web/vitest.config.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import path from "path";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
test: {
|
||||||
|
globals: false,
|
||||||
|
environment: "jsdom",
|
||||||
|
include: ["src/**/*.test.tsx", "src/**/*.test.ts"],
|
||||||
|
coverage: {
|
||||||
|
provider: "v8",
|
||||||
|
reporter: ["text", "json", "html"],
|
||||||
|
exclude: ["node_modules/", ".next/"],
|
||||||
|
},
|
||||||
|
setupFiles: ["./vitest.setup.ts"],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
1
apps/web/vitest.setup.ts
Normal file
1
apps/web/vitest.setup.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import "@testing-library/jest-dom/vitest";
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/.prettierrc.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:58
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.prettierrc.js_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.test.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:44
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.test.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.test.ts
|
||||||
|
**Tool Used:** Edit
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:27:46
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.test.ts_20260128-1327_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.test.ts
|
||||||
|
**Tool Used:** Edit
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:28:44
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.test.ts_20260128-1328_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.test.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:29:44
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.test.ts_20260128-1329_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:34
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.controller.ts
|
||||||
|
**Tool Used:** Edit
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:26:43
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.controller.ts_20260128-1326_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.module.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:33
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.module.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/app.service.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:35
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-app.service.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/src/main.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:32
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-src-main.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/vitest.config.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:46
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-vitest.config.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/vitest.config.ts
|
||||||
|
**Tool Used:** Edit
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:28:15
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-vitest.config.ts_20260128-1328_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/api/vitest.setup.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:28:13
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-api-vitest.setup.ts_20260128-1328_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/next-env.d.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:10
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-next-env.d.ts_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/next.config.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:08
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-next.config.ts_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/src/app/layout.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:23
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-src-app-layout.tsx_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/src/app/page.test.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:27:21
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-src-app-page.test.tsx_20260128-1327_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/src/app/page.test.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:30:10
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-src-app-page.test.tsx_20260128-1330_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/src/app/page.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:24
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-src-app-page.tsx_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/vitest.config.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:26
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-vitest.config.ts_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/apps/web/vitest.setup.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:21:28
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-apps-web-vitest.setup.ts_20260128-1321_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/eslint.config.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:22:06
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-eslint.config.js_20260128-1322_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/config/eslint/base.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:18:48
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-config-eslint-base.js_20260128-1318_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/config/eslint/nestjs.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:18:49
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-config-eslint-nestjs.js_20260128-1318_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/config/eslint/nextjs.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:18:48
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-config-eslint-nextjs.js_20260128-1318_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/config/prettier/index.js
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:18:50
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-config-prettier-index.js_20260128-1318_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/shared/src/index.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:26:18
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-shared-src-index.ts_20260128-1326_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/shared/src/types/index.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:23
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-shared-src-types-index.ts_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/shared/src/utils/index.test.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:25
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-shared-src-utils-index.test.ts_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/shared/src/utils/index.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:24
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-shared-src-utils-index.ts_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/shared/vitest.config.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:25
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-shared-vitest.config.ts_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/src/components/Button.test.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:59
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-src-components-Button.test.tsx_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/src/components/Button.test.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:30:08
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-src-components-Button.test.tsx_20260128-1330_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/src/components/Button.tsx
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:49
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-src-components-Button.tsx_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/src/index.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:19:48
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-src-index.ts_20260128-1319_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/vitest.config.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:00
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-vitest.config.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# QA Remediation Report
|
||||||
|
|
||||||
|
**File:** /home/localadmin/src/mosaic-stack/packages/ui/vitest.setup.ts
|
||||||
|
**Tool Used:** Write
|
||||||
|
**Epic:** general
|
||||||
|
**Iteration:** 1
|
||||||
|
**Generated:** 2026-01-28 13:20:01
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Pending QA validation
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
This report was created by the QA automation hook.
|
||||||
|
To process this report, run:
|
||||||
|
```bash
|
||||||
|
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-ui-vitest.setup.ts_20260128-1320_1_remediation_needed.md"
|
||||||
|
```
|
||||||
97
docs/scratchpads/1-project-scaffold.md
Normal file
97
docs/scratchpads/1-project-scaffold.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Issue #1: Project scaffold (monorepo, NestJS, Next.js 16)
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Set up the monorepo structure with pnpm workspaces + TurboRepo containing:
|
||||||
|
- apps/api (NestJS)
|
||||||
|
- apps/web (Next.js 16)
|
||||||
|
- packages/shared (types, utilities)
|
||||||
|
- packages/ui (shared components)
|
||||||
|
- packages/config (shared configuration)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- pnpm workspace configuration
|
||||||
|
- TurboRepo for build orchestration
|
||||||
|
- TypeScript strict mode
|
||||||
|
- ESLint + Prettier
|
||||||
|
- Vitest for unit tests
|
||||||
|
- Initial package.json scripts
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
1. Initialize root package.json with pnpm workspaces
|
||||||
|
2. Configure TurboRepo (turbo.json)
|
||||||
|
3. Set up shared packages first (config, shared, ui)
|
||||||
|
4. Create NestJS API app
|
||||||
|
5. Create Next.js 16 web app
|
||||||
|
6. Configure TypeScript, ESLint, Prettier at root level
|
||||||
|
7. Set up Vitest for testing
|
||||||
|
8. Add build/dev/test scripts
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
- [x] Initialize pnpm workspace configuration
|
||||||
|
- [x] Set up TurboRepo for build orchestration
|
||||||
|
- [x] Create packages/config
|
||||||
|
- [x] Create packages/shared
|
||||||
|
- [x] Create packages/ui
|
||||||
|
- [x] Create apps/api (NestJS)
|
||||||
|
- [x] Create apps/web (Next.js 16)
|
||||||
|
- [x] Configure TypeScript strict mode
|
||||||
|
- [x] Set up ESLint + Prettier
|
||||||
|
- [x] Configure Vitest
|
||||||
|
- [x] Add initial package.json scripts
|
||||||
|
- [x] Test build and verify
|
||||||
|
|
||||||
|
## Testing Results
|
||||||
|
- `pnpm build` - All 4 packages build successfully
|
||||||
|
- `pnpm test` - All 19 tests pass (shared: 10, api: 3, ui: 4, web: 2)
|
||||||
|
|
||||||
|
## Structure Created
|
||||||
|
```
|
||||||
|
mosaic-stack/
|
||||||
|
├── apps/
|
||||||
|
│ ├── api/ # NestJS 11.1.12 API
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── main.ts
|
||||||
|
│ │ │ ├── app.module.ts
|
||||||
|
│ │ │ ├── app.controller.ts
|
||||||
|
│ │ │ └── app.service.ts
|
||||||
|
│ │ └── package.json
|
||||||
|
│ └── web/ # Next.js 16.1.6 with App Router
|
||||||
|
│ ├── src/app/
|
||||||
|
│ │ ├── layout.tsx
|
||||||
|
│ │ ├── page.tsx
|
||||||
|
│ │ └── globals.css
|
||||||
|
│ └── package.json
|
||||||
|
├── packages/
|
||||||
|
│ ├── config/ # Shared TypeScript, ESLint, Prettier configs
|
||||||
|
│ │ ├── typescript/
|
||||||
|
│ │ ├── eslint/
|
||||||
|
│ │ └── prettier/
|
||||||
|
│ ├── shared/ # Shared types and utilities
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── types/
|
||||||
|
│ │ └── utils/
|
||||||
|
│ └── ui/ # Shared React components
|
||||||
|
│ └── src/
|
||||||
|
│ └── components/
|
||||||
|
├── package.json # Root package.json with scripts
|
||||||
|
├── pnpm-workspace.yaml # Workspace configuration
|
||||||
|
├── turbo.json # TurboRepo configuration
|
||||||
|
├── tsconfig.json # Root TypeScript config
|
||||||
|
├── eslint.config.js # Root ESLint config
|
||||||
|
└── .prettierrc.js # Root Prettier config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Scripts
|
||||||
|
- `pnpm dev` - Start all dev servers (API: 3001, Web: 3000)
|
||||||
|
- `pnpm build` - Build all packages
|
||||||
|
- `pnpm test` - Run all tests
|
||||||
|
- `pnpm lint` - Run linting
|
||||||
|
- `pnpm format` - Format all files
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Version: 0.0.1 (M1-Foundation milestone)
|
||||||
|
- Using pnpm 10.19.0 for package management
|
||||||
|
- TurboRepo 2.8.0 for efficient build caching
|
||||||
|
- Next.js 16.1.6 with Turbopack for dev
|
||||||
|
- NestJS 11.1.12 with Vitest for testing
|
||||||
|
- TypeScript 5.8.2 in strict mode
|
||||||
13
eslint.config.js
Normal file
13
eslint.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import baseConfig from "@mosaic/config/eslint/base";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: ["./tsconfig.json", "./apps/*/tsconfig.json", "./packages/*/tsconfig.json"],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "mosaic-stack",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "pnpm@10.19.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "turbo run build",
|
||||||
|
"dev": "turbo run dev",
|
||||||
|
"lint": "turbo run lint",
|
||||||
|
"lint:fix": "turbo run lint:fix",
|
||||||
|
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
||||||
|
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
||||||
|
"test": "turbo run test",
|
||||||
|
"test:watch": "turbo run test:watch",
|
||||||
|
"test:coverage": "turbo run test:coverage",
|
||||||
|
"clean": "turbo run clean && rm -rf node_modules",
|
||||||
|
"typecheck": "turbo run typecheck"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
|
"eslint": "^9.21.0",
|
||||||
|
"eslint-config-prettier": "^10.1.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"turbo": "^2.8.0",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
32
packages/config/eslint/base.js
Normal file
32
packages/config/eslint/base.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import eslint from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import prettierConfig from "eslint-config-prettier";
|
||||||
|
import prettierPlugin from "eslint-plugin-prettier";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.strictTypeChecked,
|
||||||
|
...tseslint.configs.stylisticTypeChecked,
|
||||||
|
prettierConfig,
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
prettier: prettierPlugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||||
|
],
|
||||||
|
"@typescript-eslint/consistent-type-imports": [
|
||||||
|
"error",
|
||||||
|
{ prefer: "type-imports" },
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ["**/node_modules/**", "**/dist/**", "**/.next/**", "**/coverage/**"],
|
||||||
|
}
|
||||||
|
);
|
||||||
12
packages/config/eslint/nestjs.js
Normal file
12
packages/config/eslint/nestjs.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import baseConfig from "./base.js";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/interface-name-prefix": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
17
packages/config/eslint/nextjs.js
Normal file
17
packages/config/eslint/nextjs.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import baseConfig from "./base.js";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-misused-promises": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
checksVoidReturn: {
|
||||||
|
attributes: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
29
packages/config/package.json
Normal file
29
packages/config/package.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "@mosaic/config",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"./typescript/base": "./typescript/base.json",
|
||||||
|
"./typescript/nextjs": "./typescript/nextjs.json",
|
||||||
|
"./typescript/nestjs": "./typescript/nestjs.json",
|
||||||
|
"./typescript/library": "./typescript/library.json",
|
||||||
|
"./eslint/base": "./eslint/base.js",
|
||||||
|
"./eslint/nextjs": "./eslint/nextjs.js",
|
||||||
|
"./eslint/nestjs": "./eslint/nestjs.js",
|
||||||
|
"./prettier": "./prettier/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@eslint/js": "^9.21.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
|
"eslint": "^9.21.0",
|
||||||
|
"eslint-config-prettier": "^10.1.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.8.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
packages/config/prettier/index.js
Normal file
13
packages/config/prettier/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
const config = {
|
||||||
|
semi: true,
|
||||||
|
singleQuote: false,
|
||||||
|
tabWidth: 2,
|
||||||
|
trailingComma: "es5",
|
||||||
|
printWidth: 100,
|
||||||
|
bracketSpacing: true,
|
||||||
|
arrowParens: "always",
|
||||||
|
endOfLine: "lf",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
30
packages/config/typescript/base.json
Normal file
30
packages/config/typescript/base.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictBindCallApply": true,
|
||||||
|
"strictPropertyInitialization": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022"]
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/config/typescript/library.json
Normal file
7
packages/config/typescript/library.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "./base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true
|
||||||
|
}
|
||||||
|
}
|
||||||
12
packages/config/typescript/nestjs.json
Normal file
12
packages/config/typescript/nestjs.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "./base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"incremental": true,
|
||||||
|
"target": "ES2022"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
packages/config/typescript/nextjs.json
Normal file
18
packages/config/typescript/nextjs.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "./base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||||
|
"jsx": "preserve",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowJs": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
40
packages/shared/package.json
Normal file
40
packages/shared/package.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@mosaic/shared",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"require": "./dist/index.js",
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./dist/types/index.d.ts",
|
||||||
|
"require": "./dist/types/index.js",
|
||||||
|
"import": "./dist/types/index.js"
|
||||||
|
},
|
||||||
|
"./utils": {
|
||||||
|
"types": "./dist/utils/index.d.ts",
|
||||||
|
"require": "./dist/utils/index.js",
|
||||||
|
"import": "./dist/utils/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch",
|
||||||
|
"lint": "eslint src/",
|
||||||
|
"lint:fix": "eslint src/ --fix",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mosaic/config": "workspace:*",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/shared/src/index.ts
Normal file
2
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./types/index";
|
||||||
|
export * from "./utils/index";
|
||||||
38
packages/shared/src/types/index.ts
Normal file
38
packages/shared/src/types/index.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Base entity type with common fields
|
||||||
|
*/
|
||||||
|
export interface BaseEntity {
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API response wrapper
|
||||||
|
*/
|
||||||
|
export interface ApiResponse<T> {
|
||||||
|
data: T;
|
||||||
|
success: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pagination parameters
|
||||||
|
*/
|
||||||
|
export interface PaginationParams {
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
sortBy?: string;
|
||||||
|
sortOrder?: "asc" | "desc";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paginated response
|
||||||
|
*/
|
||||||
|
export interface PaginatedResponse<T> {
|
||||||
|
data: T[];
|
||||||
|
total: number;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
totalPages: number;
|
||||||
|
}
|
||||||
61
packages/shared/src/utils/index.test.ts
Normal file
61
packages/shared/src/utils/index.test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { isDefined, parseDate, sleep, slugify } from "./index.js";
|
||||||
|
|
||||||
|
describe("parseDate", () => {
|
||||||
|
it("should return undefined for null or undefined", () => {
|
||||||
|
expect(parseDate(null)).toBeUndefined();
|
||||||
|
expect(parseDate(undefined)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the same Date object if passed a Date", () => {
|
||||||
|
const date = new Date("2024-01-15");
|
||||||
|
expect(parseDate(date)).toBe(date);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse valid date strings", () => {
|
||||||
|
const result = parseDate("2024-01-15");
|
||||||
|
expect(result).toBeInstanceOf(Date);
|
||||||
|
expect(result?.toISOString()).toContain("2024-01-15");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return undefined for invalid date strings", () => {
|
||||||
|
expect(parseDate("not-a-date")).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("slugify", () => {
|
||||||
|
it("should convert text to lowercase slug", () => {
|
||||||
|
expect(slugify("Hello World")).toBe("hello-world");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle special characters", () => {
|
||||||
|
expect(slugify("Hello, World!")).toBe("hello-world");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trim leading and trailing hyphens", () => {
|
||||||
|
expect(slugify(" Hello World ")).toBe("hello-world");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("sleep", () => {
|
||||||
|
it("should resolve after the specified time", async () => {
|
||||||
|
const start = Date.now();
|
||||||
|
await sleep(50);
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
expect(elapsed).toBeGreaterThanOrEqual(45);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isDefined", () => {
|
||||||
|
it("should return false for null and undefined", () => {
|
||||||
|
expect(isDefined(null)).toBe(false);
|
||||||
|
expect(isDefined(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for defined values", () => {
|
||||||
|
expect(isDefined(0)).toBe(true);
|
||||||
|
expect(isDefined("")).toBe(true);
|
||||||
|
expect(isDefined(false)).toBe(true);
|
||||||
|
expect(isDefined({})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
35
packages/shared/src/utils/index.ts
Normal file
35
packages/shared/src/utils/index.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Safely parse a date string or return undefined
|
||||||
|
*/
|
||||||
|
export function parseDate(value: string | Date | undefined | null): Date | undefined {
|
||||||
|
if (!value) return undefined;
|
||||||
|
if (value instanceof Date) return value;
|
||||||
|
const parsed = new Date(value);
|
||||||
|
return isNaN(parsed.getTime()) ? undefined : parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a simple slug from a string
|
||||||
|
*/
|
||||||
|
export function slugify(text: string): string {
|
||||||
|
return text
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
.replace(/[^\w\s-]/g, "")
|
||||||
|
.replace(/[\s_-]+/g, "-")
|
||||||
|
.replace(/^-+|-+$/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep for a given number of milliseconds
|
||||||
|
*/
|
||||||
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a value is defined (not null or undefined)
|
||||||
|
*/
|
||||||
|
export function isDefined<T>(value: T | null | undefined): value is T {
|
||||||
|
return value !== null && value !== undefined;
|
||||||
|
}
|
||||||
12
packages/shared/tsconfig.json
Normal file
12
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@mosaic/config/typescript/library",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": ["ES2022", "DOM"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||||
|
}
|
||||||
14
packages/shared/vitest.config.ts
Normal file
14
packages/shared/vitest.config.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: false,
|
||||||
|
environment: "node",
|
||||||
|
include: ["src/**/*.test.ts"],
|
||||||
|
coverage: {
|
||||||
|
provider: "v8",
|
||||||
|
reporter: ["text", "json", "html"],
|
||||||
|
exclude: ["node_modules/", "dist/"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
42
packages/ui/package.json
Normal file
42
packages/ui/package.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "@mosaic/ui",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./components/*": {
|
||||||
|
"types": "./dist/components/*.d.ts",
|
||||||
|
"import": "./dist/components/*.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch",
|
||||||
|
"lint": "eslint src/",
|
||||||
|
"lint:fix": "eslint src/ --fix",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^19.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mosaic/config": "workspace:*",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.2.0",
|
||||||
|
"@types/react": "^19.0.8",
|
||||||
|
"jsdom": "^26.0.0",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vitest": "^3.0.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
packages/ui/src/components/Button.test.tsx
Normal file
31
packages/ui/src/components/Button.test.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { describe, expect, it, afterEach } from "vitest";
|
||||||
|
import { render, screen, cleanup } from "@testing-library/react";
|
||||||
|
import { Button } from "./Button.js";
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Button", () => {
|
||||||
|
it("should render children", () => {
|
||||||
|
render(<Button>Click me</Button>);
|
||||||
|
expect(screen.getByRole("button")).toHaveTextContent("Click me");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply variant styles", () => {
|
||||||
|
render(<Button variant="danger">Delete</Button>);
|
||||||
|
const button = screen.getByRole("button");
|
||||||
|
expect(button.className).toContain("bg-red-600");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply size styles", () => {
|
||||||
|
render(<Button size="lg">Large Button</Button>);
|
||||||
|
const button = screen.getByRole("button");
|
||||||
|
expect(button.className).toContain("px-6");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pass through additional props", () => {
|
||||||
|
render(<Button disabled>Disabled</Button>);
|
||||||
|
expect(screen.getByRole("button")).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user