Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .clinerules/01-ResponceType.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ Project follows a standardized response type for all API endpoints.
- **errors**: Field-specific validation errors (maps field names to arrays of messages)
- **data**: The actual response data (success responses only)
- **total, page, limit**: Only for paginated responses


## Examples:

### Success Response (Single Item)

```json
{
"data": {
Expand All @@ -45,10 +46,7 @@ Project follows a standardized response type for all API endpoints.

```json
{
"data": [
{"id": "1", "name": "Toyota"},
{"id": "2", "name": "Honda"}
],
"data": [{ "id": "1", "name": "Toyota" }, { "id": "2", "name": "Honda" }],
"total": 150,
"page": 1,
"limit": 30
Expand Down Expand Up @@ -79,7 +77,6 @@ Project follows a standardized response type for all API endpoints.

### Combined Error (with both message and field errors)


```json
{
"message": "Brand already exists",
Expand All @@ -103,7 +100,7 @@ Project follows a standardized response type for all API endpoints.

- All user-facing messages go through `req.appInfo.i18n?.t()`, and the English
text belongs in i18next's own `defaultValue` option — **never behind `||`**.
A missing key makes `t()` return the key *itself*, which is a truthy string,
A missing key makes `t()` return the key _itself_, which is a truthy string,
so `t('x') || 'fallback'` never reaches the fallback: it answers with the raw
`x`. `defaultValue` is what actually renders English on a miss.
- Example: `req.appInfo.i18n?.t('validation.required', { defaultValue: 'Field is required' })`
Expand All @@ -122,7 +119,10 @@ Project follows a standardized response type for all API endpoints.
## Field-Specific Error Examples:

```javascript
import { BadRequestError, ConflictError } from '@adaptivestone/framework/services/http/httpErrors.js';
import {
BadRequestError,
ConflictError,
} from '@adaptivestone/framework/services/http/httpErrors.js';

// File validation error → 400 { message, errors: { image: [...] } }
throw new BadRequestError({
Expand Down
13 changes: 13 additions & 0 deletions .clinerules/02-DockerCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This project runs inside Docker containers. All commands must be executed within
## Command Execution Format

### Backend Commands

For any backend-related commands (TypeScript, Node.js, npm, etc.), use:
```bash
docker compose exec backend {command}
Expand All @@ -13,23 +14,27 @@ docker compose exec backend {command}
### Examples:

#### TypeScript Type Check

```bash
docker compose exec backend npm run check:types
```

#### Generate Types (Required after Model Changes)

**IMPORTANT**: After creating or updating any model, you MUST regenerate types:
```bash
docker compose exec backend npm run generateTypes
```
This generates type definitions that allow `this.app.getModel('ModelName')` to work with proper types without manual casting.

#### Install Dependencies

```bash
docker compose exec backend npm install
```

#### Run Tests

```bash
docker compose exec backend npm test
```
Expand Down Expand Up @@ -68,23 +73,27 @@ docker compose run --rm backend-bun bash scripts/bun-test.sh --test-name-pattern
Bun never runs on the host. See [11-Testing.md](./11-Testing.md).

#### Run Development Server

```bash
docker compose exec backend npm run dev
```

#### Database Migrations

Create a migration (scaffolds the file — never hand-write one), then apply pending ones. See [10-Migrations.md](./10-Migrations.md).
```bash
docker compose exec backend npm run cli migration/create -- --name=someName
docker compose exec backend npm run cli migration/migrate
```

#### Linting

```bash
docker compose exec backend npm run lint
```

#### Build Project

```bash
docker compose exec backend npm run build
```
Expand All @@ -97,24 +106,28 @@ docker compose exec backend npm run build
4. **Use the correct service name** (`backend` for this project)

## Container Status Check

To check if containers are running:
```bash
docker compose ps
```

## Starting Containers

If containers are not running:
```bash
docker compose up
```

## Accessing Container Shell

For interactive debugging:
```bash
docker compose exec backend bash
```

## Database Access

If database operations are needed:
```bash
docker compose exec mongo mongosh
Expand Down
25 changes: 22 additions & 3 deletions .clinerules/03-ControllerPattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,14 @@ export default YourController;
## Route Definition Rules

### HTTP Methods

- `get`: For retrieving data
- `post`: For creating new resources
- `put`: For updating existing resources
- `delete`: For deleting resources

### Route Structure

```typescript
get: {
'/path': {
Expand All @@ -135,6 +137,7 @@ post: {
### Common Middleware

#### Pagination

```typescript
import Pagination from '@adaptivestone/framework/services/http/middleware/Pagination.js';

Expand All @@ -152,14 +155,16 @@ async getList(
```

#### Role-based Access Control

```typescript
import RoleMiddleware from '@adaptivestone/framework/services/http/middleware/Role.js';

// In routes
middleware: [[RoleMiddleware, { roles: ['admin'] }]]
middleware: [[RoleMiddleware, { roles: ['admin'] }]];
```

#### Authentication

```typescript
import Auth from '@adaptivestone/framework/services/http/middleware/Auth.js';
import GetUserByToken from '@adaptivestone/framework/services/http/middleware/GetUserByToken.js';
Expand All @@ -175,24 +180,26 @@ static get middleware() {
Any [Standard Schema](https://standardschema.dev/) validator works as a route `query:` / `request:` schema. The `<Output>` you declare is what codegen turns into `req.appInfo.query` / `req.appInfo.request`.

### Default — zod

```typescript
import { z } from 'zod';

query: z.object({
status: z.string().optional(),
tags: z.array(z.string()).optional(),
})
});
```

### Other validators (yup / valibot / arktype)

Any Standard Schema validator works the same way — pick one and import it. Yup is the framework's traditional choice (yup ≥1.7 implements Standard Schema):
```typescript
import { object, string, array } from 'yup';

request: object().shape({
name: string().required(),
tags: array().of(string()),
})
});
```

> File uploads: import `File` from `@adaptivestone/framework/types.js` and use
Expand All @@ -204,6 +211,7 @@ request: object().shape({
> The signatures below predate codegen and hand-write the `appInfo` shape for illustration. In real controllers, type each handler with the generated `…Request` type (PascalCased handler name) from `./<Controller>.routes.gen.ts` (see the Basic Template) instead of `FrameworkRequest & { appInfo: { ... } }` — the generated type already includes `pagination`, `query`, `request`, `params`, and `user`.

### Basic GET Handler

```typescript
async getList(
req: FrameworkRequest & PaginationMiddlewareAppInfo & {
Expand Down Expand Up @@ -247,6 +255,7 @@ async getList(
```

### GET by Slug Handler

```typescript
async getBySlug(req: FrameworkRequest, res: Response) {
const Model = this.app.getModel('ModelName');
Expand All @@ -267,6 +276,7 @@ async getBySlug(req: FrameworkRequest, res: Response) {
```

### POST (Create) Handler

```typescript
async create(
req: FrameworkRequest & {
Expand Down Expand Up @@ -342,6 +352,7 @@ async create(
```

### PUT (Update) Handler

```typescript
async update(
req: FrameworkRequest & {
Expand Down Expand Up @@ -415,6 +426,7 @@ async update(
```

### DELETE Handler

```typescript
async delete(req: FrameworkRequest, res: Response) {
const Model = this.app.getModel('ModelName');
Expand All @@ -439,11 +451,13 @@ async delete(req: FrameworkRequest, res: Response) {
## Important Rules

### 1. Response Format

- Always follow the project's response format (see `.clinerules/01-ResponceType.md`)
- Use proper HTTP status codes
- Include internationalization support

### 2. Error Handling

- Framework handles errors automatically (see `.clinerules/07-ErrorHandling.md`)
- Do NOT wrap main handler logic in try-catch blocks
- Only use try-catch for specific non-critical operations (external APIs, logging, etc.)
Expand All @@ -452,24 +466,29 @@ async delete(req: FrameworkRequest, res: Response) {
- Use field-specific errors in the `errors` object

### 3. Model Access

- Use `this.app.getModel('ModelName')` to access models
- Always call `getPublic()` on model instances before returning

### 4. Validation

- Use Yup schemas for request/query validation
- Validate file uploads properly
- Check for duplicates before creating/updating

### 5. Pagination

- Use `Pagination` middleware for list endpoints
- Always return `total`, `page`, and `limit` for paginated responses

### 6. File Uploads

- Validate file types and presence
- Use `FileModel.uploadFile()` for file handling
- Store file references in the database

### 7. Internationalization

- Use `req.appInfo.i18n?.t()` for all user-facing messages
- Carry the English text in i18next's `defaultValue` option —
`t('key', { defaultValue: 'English text' })`. Never `t('key') || 'English
Expand Down
23 changes: 15 additions & 8 deletions .clinerules/05-TS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project follows strict TypeScript standards to ensure type safety and code
**Rule**: Avoid using the `any` type as much as possible. TypeScript's strength lies in its type system, and using `any` defeats this purpose.

### ❌ Incorrect Usage

```typescript
function processData(data: any): any {
return data.someProperty;
Expand All @@ -16,6 +17,7 @@ const result: any = someFunction();
```

### ✅ Correct Usage

```typescript
interface DataType {
someProperty: string;
Expand All @@ -32,6 +34,7 @@ const result: DataType = someFunction();
## Type Definitions

### Models Should Provide Types

- Models should export proper TypeScript types for all their properties
- Use the model's exported types instead of `any`
- Keep the readonly Mongoose schema as the single source of truth; do not copy it
Expand Down Expand Up @@ -73,17 +76,19 @@ validation or authorization; it throws if the model is unavailable instead of
returning `false`.

### When Types Are Unknown

If the exact type is unknown, use more specific alternatives:

```typescript
// Instead of 'any', use:
unknown // For truly unknown data that needs type checking
object // For objects
Record<string, unknown> // For object with string keys
Array<unknown> // For arrays of unknown items
unknown; // For truly unknown data that needs type checking
object; // For objects
Record<string, unknown>; // For object with string keys
Array<unknown>; // For arrays of unknown items
```

### Database Documents

```typescript
// ❌ Don't use any
const doc: any = await Model.findOne();
Expand All @@ -93,6 +98,7 @@ const doc: InstanceType<TModelType> | null = await Model.findOne();
```

### API Responses

```typescript
// ❌ Don't use any
const response: any = await fetch('/api/data');
Expand All @@ -107,6 +113,7 @@ const response: ApiResponse = await fetch('/api/data');
```

### Error Handling

```typescript
// ❌ Don't use any
catch (error: any) {
Expand Down Expand Up @@ -144,16 +151,16 @@ Use TypeScript's built-in utility types:

```typescript
// For partial updates
Partial<ModelType>
Partial<ModelType>;

// For picking specific properties
Pick<ModelType, 'prop1' | 'prop2'>
Pick<ModelType, 'prop1' | 'prop2'>;

// For omitting properties
Omit<ModelType, 'sensitiveField'>
Omit<ModelType, 'sensitiveField'>;

// For making properties optional
Optional<ModelType, 'optionalField'>
Optional<ModelType, 'optionalField'>;
```

## Important Rules
Expand Down
Loading
Loading