mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 20:12:38 +08:00
refactor(dataApi): streamline Data API schema and type definitions
- Removed outdated API model and schema files to simplify the structure. - Consolidated API types and schemas for better organization and clarity. - Updated import paths across the codebase to reflect the new structure. - Enhanced documentation in related README files to guide usage of the new API schema organization.
This commit is contained in:
parent
8292958c0d
commit
18df6085d7
@ -91,9 +91,7 @@
|
||||
"indexes": {
|
||||
"entity_tag_tag_id_idx": {
|
||||
"name": "entity_tag_tag_id_idx",
|
||||
"columns": [
|
||||
"tag_id"
|
||||
],
|
||||
"columns": ["tag_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@ -102,23 +100,15 @@
|
||||
"name": "entity_tag_tag_id_tag_id_fk",
|
||||
"tableFrom": "entity_tag",
|
||||
"tableTo": "tag",
|
||||
"columnsFrom": [
|
||||
"tag_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["tag_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"entity_tag_entity_type_entity_id_tag_id_pk": {
|
||||
"columns": [
|
||||
"entity_type",
|
||||
"entity_id",
|
||||
"tag_id"
|
||||
],
|
||||
"columns": ["entity_type", "entity_id", "tag_id"],
|
||||
"name": "entity_tag_entity_type_entity_id_tag_id_pk"
|
||||
}
|
||||
},
|
||||
@ -175,10 +165,7 @@
|
||||
"indexes": {
|
||||
"group_entity_sort_idx": {
|
||||
"name": "group_entity_sort_idx",
|
||||
"columns": [
|
||||
"entity_type",
|
||||
"sort_order"
|
||||
],
|
||||
"columns": ["entity_type", "sort_order"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@ -314,24 +301,17 @@
|
||||
"indexes": {
|
||||
"message_parent_id_idx": {
|
||||
"name": "message_parent_id_idx",
|
||||
"columns": [
|
||||
"parent_id"
|
||||
],
|
||||
"columns": ["parent_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"message_topic_created_idx": {
|
||||
"name": "message_topic_created_idx",
|
||||
"columns": [
|
||||
"topic_id",
|
||||
"created_at"
|
||||
],
|
||||
"columns": ["topic_id", "created_at"],
|
||||
"isUnique": false
|
||||
},
|
||||
"message_trace_id_idx": {
|
||||
"name": "message_trace_id_idx",
|
||||
"columns": [
|
||||
"trace_id"
|
||||
],
|
||||
"columns": ["trace_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@ -340,12 +320,8 @@
|
||||
"name": "message_topic_id_topic_id_fk",
|
||||
"tableFrom": "message",
|
||||
"tableTo": "topic",
|
||||
"columnsFrom": [
|
||||
"topic_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["topic_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
@ -353,12 +329,8 @@
|
||||
"name": "message_parent_id_message_id_fk",
|
||||
"tableFrom": "message",
|
||||
"tableTo": "message",
|
||||
"columnsFrom": [
|
||||
"parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["parent_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
@ -420,10 +392,7 @@
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"preference_scope_key_pk": {
|
||||
"columns": [
|
||||
"scope",
|
||||
"key"
|
||||
],
|
||||
"columns": ["scope", "key"],
|
||||
"name": "preference_scope_key_pk"
|
||||
}
|
||||
},
|
||||
@ -472,9 +441,7 @@
|
||||
"indexes": {
|
||||
"tag_name_unique": {
|
||||
"name": "tag_name_unique",
|
||||
"columns": [
|
||||
"name"
|
||||
],
|
||||
"columns": ["name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@ -585,40 +552,27 @@
|
||||
"indexes": {
|
||||
"topic_group_updated_idx": {
|
||||
"name": "topic_group_updated_idx",
|
||||
"columns": [
|
||||
"group_id",
|
||||
"updated_at"
|
||||
],
|
||||
"columns": ["group_id", "updated_at"],
|
||||
"isUnique": false
|
||||
},
|
||||
"topic_group_sort_idx": {
|
||||
"name": "topic_group_sort_idx",
|
||||
"columns": [
|
||||
"group_id",
|
||||
"sort_order"
|
||||
],
|
||||
"columns": ["group_id", "sort_order"],
|
||||
"isUnique": false
|
||||
},
|
||||
"topic_updated_at_idx": {
|
||||
"name": "topic_updated_at_idx",
|
||||
"columns": [
|
||||
"updated_at"
|
||||
],
|
||||
"columns": ["updated_at"],
|
||||
"isUnique": false
|
||||
},
|
||||
"topic_is_pinned_idx": {
|
||||
"name": "topic_is_pinned_idx",
|
||||
"columns": [
|
||||
"is_pinned",
|
||||
"pinned_order"
|
||||
],
|
||||
"columns": ["is_pinned", "pinned_order"],
|
||||
"isUnique": false
|
||||
},
|
||||
"topic_assistant_id_idx": {
|
||||
"name": "topic_assistant_id_idx",
|
||||
"columns": [
|
||||
"assistant_id"
|
||||
],
|
||||
"columns": ["assistant_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@ -627,12 +581,8 @@
|
||||
"name": "topic_group_id_group_id_fk",
|
||||
"tableFrom": "topic",
|
||||
"tableTo": "group",
|
||||
"columnsFrom": [
|
||||
"group_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["group_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
@ -652,4 +602,4 @@
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,4 +17,4 @@
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,17 +2,20 @@
|
||||
|
||||
This directory contains shared type definitions and schemas for the Cherry Studio data management systems. These files provide type safety and consistency across the entire application.
|
||||
|
||||
## 📁 Directory Structure
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
packages/shared/data/
|
||||
├── api/ # Data API type system
|
||||
│ ├── index.ts # Barrel exports for clean imports
|
||||
│ ├── apiSchemas.ts # API endpoint definitions and mappings
|
||||
│ ├── apiTypes.ts # Core request/response infrastructure types
|
||||
│ ├── apiModels.ts # Business entity types and DTOs
|
||||
│ ├── apiPaths.ts # API path definitions and utilities
|
||||
│ └── errorCodes.ts # Standardized error handling
|
||||
├── api/ # Data API type system (see api/README.md)
|
||||
│ ├── index.ts # Barrel exports for infrastructure types
|
||||
│ ├── apiTypes.ts # Core request/response types and utilities
|
||||
│ ├── apiPaths.ts # Path template literal type utilities
|
||||
│ ├── errorCodes.ts # Error handling utilities
|
||||
│ ├── schemas/ # Domain-specific API schemas
|
||||
│ │ ├── index.ts # Schema composition
|
||||
│ │ ├── test.ts # Test API schema and DTOs
|
||||
│ │ └── batch.ts # Batch/transaction operations
|
||||
│ └── README.md # Detailed API documentation
|
||||
├── cache/ # Cache system type definitions
|
||||
│ ├── cacheTypes.ts # Core cache infrastructure types
|
||||
│ ├── cacheSchemas.ts # Cache key schemas and type mappings
|
||||
@ -24,7 +27,7 @@ packages/shared/data/
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 🏗️ System Overview
|
||||
## System Overview
|
||||
|
||||
This directory provides type definitions for four main data management systems:
|
||||
|
||||
@ -35,8 +38,8 @@ This directory provides type definitions for four main data management systems:
|
||||
|
||||
### API System (`api/`)
|
||||
- **Purpose**: Type-safe IPC communication between Main and Renderer processes
|
||||
- **Features**: RESTful patterns, error handling, business entity definitions
|
||||
- **Usage**: Ensures type safety for all data API operations
|
||||
- **Features**: RESTful patterns, modular schema design, error handling
|
||||
- **Documentation**: See [`api/README.md`](./api/README.md) for detailed usage
|
||||
|
||||
### Cache System (`cache/`)
|
||||
- **Purpose**: Type definitions for three-layer caching architecture
|
||||
@ -48,7 +51,7 @@ This directory provides type definitions for four main data management systems:
|
||||
- **Features**: 158 configuration items, default values, nested key support
|
||||
- **Usage**: Type-safe preference access and synchronization
|
||||
|
||||
## 📋 File Categories
|
||||
## File Categories
|
||||
|
||||
**Framework Infrastructure** - These are TypeScript type definitions that:
|
||||
- ✅ Exist only at compile time
|
||||
@ -56,12 +59,16 @@ This directory provides type definitions for four main data management systems:
|
||||
- ✅ Define contracts between application layers
|
||||
- ✅ Enable static analysis and error detection
|
||||
|
||||
## 📖 Usage Examples
|
||||
## Usage Examples
|
||||
|
||||
### API Types
|
||||
```typescript
|
||||
// Import API types
|
||||
import type { DataRequest, DataResponse, ApiSchemas } from '@shared/data/api'
|
||||
// Infrastructure types from barrel export
|
||||
import type { DataRequest, DataResponse, ApiClient } from '@shared/data/api'
|
||||
import { DataApiErrorFactory, ErrorCode } from '@shared/data/api'
|
||||
|
||||
// Domain DTOs directly from schema files
|
||||
import type { TestItem, CreateTestItemDto } from '@shared/data/api/schemas/test'
|
||||
```
|
||||
|
||||
### Cache Types
|
||||
@ -76,7 +83,7 @@ import type { UseCacheKey, UseSharedCacheKey } from '@shared/data/cache'
|
||||
import type { PreferenceKeyType, PreferenceDefaultScopeType } from '@shared/data/preference'
|
||||
```
|
||||
|
||||
## 🔧 Development Guidelines
|
||||
## Development Guidelines
|
||||
|
||||
### Adding Shared Types
|
||||
1. Create or update type file in `types/` directory
|
||||
@ -94,24 +101,25 @@ import type { PreferenceKeyType, PreferenceDefaultScopeType } from '@shared/data
|
||||
3. Preference system automatically picks up new keys
|
||||
|
||||
### Adding API Types
|
||||
1. Define business entities in `api/apiModels.ts`
|
||||
2. Add endpoint definitions to `api/apiSchemas.ts`
|
||||
3. Export types from `api/index.ts`
|
||||
1. Create schema file in `api/schemas/` (e.g., `topic.ts`)
|
||||
2. Define domain models, DTOs, and API schema in the file
|
||||
3. Register schema in `api/schemas/index.ts` using intersection type
|
||||
4. See [`api/README.md`](./api/README.md) for detailed guide
|
||||
|
||||
### Best Practices
|
||||
- Use `import type` for type-only imports
|
||||
- Infrastructure types from barrel, domain DTOs from schema files
|
||||
- Follow existing naming conventions
|
||||
- Document complex types with JSDoc
|
||||
- Maintain type safety across all imports
|
||||
|
||||
## 🔗 Related Implementation
|
||||
## Related Implementation
|
||||
|
||||
### Main Process Services
|
||||
- `src/main/data/CacheService.ts` - Main process cache management
|
||||
- `src/main/data/PreferenceService.ts` - Preference management service
|
||||
- `src/main/data/DataApiService.ts` - Data API coordination service
|
||||
### Main Process
|
||||
- `src/main/data/api/` - API server, handlers, and IPC adapter
|
||||
- `src/main/data/cache/` - Cache service implementation
|
||||
- `src/main/data/preference/` - Preference service implementation
|
||||
|
||||
### Renderer Process Services
|
||||
- `src/renderer/src/data/CacheService.ts` - Renderer cache service
|
||||
- `src/renderer/src/data/PreferenceService.ts` - Renderer preference service
|
||||
- `src/renderer/src/data/DataApiService.ts` - Renderer API client
|
||||
### Renderer Process
|
||||
- `src/renderer/src/services/DataApiService.ts` - API client
|
||||
- `src/renderer/src/services/CacheService.ts` - Cache service
|
||||
- `src/renderer/src/services/PreferenceService.ts` - Preference service
|
||||
189
packages/shared/data/api/README.md
Normal file
189
packages/shared/data/api/README.md
Normal file
@ -0,0 +1,189 @@
|
||||
# Data API Type System
|
||||
|
||||
This directory contains the type definitions and utilities for Cherry Studio's Data API system, which provides type-safe IPC communication between renderer and main processes.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
packages/shared/data/api/
|
||||
├── index.ts # Barrel export for infrastructure types
|
||||
├── apiTypes.ts # Core request/response types and API utilities
|
||||
├── apiPaths.ts # Path template literal type utilities
|
||||
├── errorCodes.ts # Error handling utilities and factories
|
||||
└── schemas/
|
||||
├── index.ts # Schema composition (merges all domain schemas)
|
||||
├── test.ts # Test API schema and DTOs
|
||||
└── batch.ts # Batch/transaction API schema
|
||||
```
|
||||
|
||||
## File Responsibilities
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `apiTypes.ts` | Core types (`DataRequest`, `DataResponse`, `ApiClient`) and schema utilities |
|
||||
| `apiPaths.ts` | Template literal types for path resolution (`/items/:id` → `/items/${string}`) |
|
||||
| `errorCodes.ts` | `DataApiErrorFactory`, error codes, and error handling utilities |
|
||||
| `index.ts` | Unified export of infrastructure types (not domain DTOs) |
|
||||
| `schemas/index.ts` | Composes all domain schemas into `ApiSchemas` using intersection types |
|
||||
| `schemas/*.ts` | Domain-specific API definitions and DTOs |
|
||||
|
||||
## Import Conventions
|
||||
|
||||
### Infrastructure Types (via barrel export)
|
||||
|
||||
Use the barrel export for common API infrastructure:
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
DataRequest,
|
||||
DataResponse,
|
||||
ApiClient,
|
||||
PaginatedResponse,
|
||||
ErrorCode
|
||||
} from '@shared/data/api'
|
||||
|
||||
import { DataApiErrorFactory, isDataApiError } from '@shared/data/api'
|
||||
```
|
||||
|
||||
### Domain DTOs (directly from schema files)
|
||||
|
||||
Import domain-specific types directly from their schema files:
|
||||
|
||||
```typescript
|
||||
// Topic domain
|
||||
import type { Topic, CreateTopicDto, UpdateTopicDto } from '@shared/data/api/schemas/topic'
|
||||
|
||||
// Message domain
|
||||
import type { Message, CreateMessageDto } from '@shared/data/api/schemas/message'
|
||||
|
||||
// Test domain (development)
|
||||
import type { TestItem, CreateTestItemDto } from '@shared/data/api/schemas/test'
|
||||
```
|
||||
|
||||
## Adding a New Domain Schema
|
||||
|
||||
1. Create the schema file (e.g., `schemas/topic.ts`):
|
||||
|
||||
```typescript
|
||||
import type { PaginatedResponse } from '../apiTypes'
|
||||
|
||||
// Domain models
|
||||
export interface Topic {
|
||||
id: string
|
||||
name: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface CreateTopicDto {
|
||||
name: string
|
||||
}
|
||||
|
||||
// API Schema - validation happens via AssertValidSchemas in index.ts
|
||||
export interface TopicSchemas {
|
||||
'/topics': {
|
||||
GET: {
|
||||
response: PaginatedResponse<Topic> // response is required
|
||||
}
|
||||
POST: {
|
||||
body: CreateTopicDto
|
||||
response: Topic
|
||||
}
|
||||
}
|
||||
'/topics/:id': {
|
||||
GET: {
|
||||
params: { id: string }
|
||||
response: Topic
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Validation**: Schemas are validated at composition level via `AssertValidSchemas` in `schemas/index.ts`:
|
||||
- Ensures only valid HTTP methods (GET, POST, PUT, DELETE, PATCH)
|
||||
- Requires `response` field for each endpoint
|
||||
- Invalid schemas cause TypeScript errors at the composition point
|
||||
|
||||
2. Register in `schemas/index.ts`:
|
||||
|
||||
```typescript
|
||||
import type { TopicSchemas } from './topic'
|
||||
|
||||
// AssertValidSchemas provides fallback validation even if ValidateSchema is forgotten
|
||||
export type ApiSchemas = AssertValidSchemas<TestSchemas & BatchSchemas & TopicSchemas>
|
||||
```
|
||||
|
||||
3. Implement handlers in `src/main/data/api/handlers/`
|
||||
|
||||
## Type Safety Features
|
||||
|
||||
### Path Resolution
|
||||
|
||||
The system uses template literal types to map concrete paths to schema paths:
|
||||
|
||||
```typescript
|
||||
// Concrete path '/topics/abc123' maps to schema path '/topics/:id'
|
||||
api.get('/topics/abc123') // TypeScript knows this returns Topic
|
||||
```
|
||||
|
||||
### Exhaustive Handler Checking
|
||||
|
||||
`ApiImplementation` type ensures all schema endpoints have handlers:
|
||||
|
||||
```typescript
|
||||
// TypeScript will error if any endpoint is missing
|
||||
const handlers: ApiImplementation = {
|
||||
'/topics': {
|
||||
GET: async () => { /* ... */ },
|
||||
POST: async ({ body }) => { /* ... */ }
|
||||
}
|
||||
// Missing '/topics/:id' would cause compile error
|
||||
}
|
||||
```
|
||||
|
||||
### Type-Safe Client
|
||||
|
||||
`ApiClient` provides fully typed methods:
|
||||
|
||||
```typescript
|
||||
const topic = await api.get('/topics/123') // Returns Topic
|
||||
const topics = await api.get('/topics', { query: { page: 1 } }) // Returns PaginatedResponse<Topic>
|
||||
await api.post('/topics', { body: { name: 'New' } }) // Body is typed as CreateTopicDto
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Use `DataApiErrorFactory` for consistent error creation:
|
||||
|
||||
```typescript
|
||||
import { DataApiErrorFactory, ErrorCode } from '@shared/data/api'
|
||||
|
||||
// Create errors
|
||||
throw DataApiErrorFactory.notFound('Topic', id)
|
||||
throw DataApiErrorFactory.validationError('Name is required')
|
||||
throw DataApiErrorFactory.fromCode(ErrorCode.DATABASE_ERROR, 'Connection failed')
|
||||
|
||||
// Check errors
|
||||
if (isDataApiError(error)) {
|
||||
console.log(error.code, error.status)
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
Renderer Main
|
||||
────────────────────────────────────────────────────
|
||||
DataApiService ──IPC──► IpcAdapter ──► ApiServer
|
||||
│ │
|
||||
│ ▼
|
||||
ApiClient MiddlewareEngine
|
||||
(typed) │
|
||||
▼
|
||||
Handlers
|
||||
(typed)
|
||||
```
|
||||
|
||||
- **Renderer**: Uses `DataApiService` with type-safe `ApiClient` interface
|
||||
- **IPC**: Requests serialized via `IpcAdapter`
|
||||
- **Main**: `ApiServer` routes to handlers through `MiddlewareEngine`
|
||||
- **Type Safety**: End-to-end types from client call to handler implementation
|
||||
@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Generic test model definitions
|
||||
* Contains flexible types for comprehensive API testing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic test item entity - flexible structure for testing various scenarios
|
||||
*/
|
||||
export interface TestItem {
|
||||
/** Unique identifier */
|
||||
id: string
|
||||
/** Item title */
|
||||
title: string
|
||||
/** Optional description */
|
||||
description?: string
|
||||
/** Type category */
|
||||
type: string
|
||||
/** Current status */
|
||||
status: string
|
||||
/** Priority level */
|
||||
priority: string
|
||||
/** Associated tags */
|
||||
tags: string[]
|
||||
/** Creation timestamp */
|
||||
createdAt: string
|
||||
/** Last update timestamp */
|
||||
updatedAt: string
|
||||
/** Additional metadata */
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Transfer Objects (DTOs) for test operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* DTO for creating a new test item
|
||||
*/
|
||||
export interface CreateTestItemDto {
|
||||
/** Item title */
|
||||
title: string
|
||||
/** Optional description */
|
||||
description?: string
|
||||
/** Type category */
|
||||
type?: string
|
||||
/** Current status */
|
||||
status?: string
|
||||
/** Priority level */
|
||||
priority?: string
|
||||
/** Associated tags */
|
||||
tags?: string[]
|
||||
/** Additional metadata */
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO for updating an existing test item
|
||||
*/
|
||||
export interface UpdateTestItemDto {
|
||||
/** Updated title */
|
||||
title?: string
|
||||
/** Updated description */
|
||||
description?: string
|
||||
/** Updated type */
|
||||
type?: string
|
||||
/** Updated status */
|
||||
status?: string
|
||||
/** Updated priority */
|
||||
priority?: string
|
||||
/** Updated tags */
|
||||
tags?: string[]
|
||||
/** Updated metadata */
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk operation types for batch processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request for bulk operations on multiple items
|
||||
*/
|
||||
export interface BulkOperationRequest<TData = any> {
|
||||
/** Type of bulk operation to perform */
|
||||
operation: 'create' | 'update' | 'delete' | 'archive' | 'restore'
|
||||
/** Array of data items to process */
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from a bulk operation
|
||||
*/
|
||||
export interface BulkOperationResponse {
|
||||
/** Number of successfully processed items */
|
||||
successful: number
|
||||
/** Number of items that failed processing */
|
||||
failed: number
|
||||
/** Array of errors that occurred during processing */
|
||||
errors: Array<{
|
||||
/** Index of the item that failed */
|
||||
index: number
|
||||
/** Error message */
|
||||
error: string
|
||||
/** Optional additional error data */
|
||||
data?: any
|
||||
}>
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ApiSchemas } from './apiSchemas'
|
||||
import type { ApiSchemas } from './schemas'
|
||||
|
||||
/**
|
||||
* Template literal type utilities for converting parameterized paths to concrete paths
|
||||
|
||||
@ -1,487 +0,0 @@
|
||||
// NOTE: Types are defined inline in the schema for simplicity
|
||||
// If needed, specific types can be imported from './apiModels'
|
||||
import type { BodyForPath, ConcreteApiPaths, QueryParamsForPath, ResponseForPath } from './apiPaths'
|
||||
import type { HttpMethod, PaginatedResponse, PaginationParams } from './apiTypes'
|
||||
|
||||
// Re-export for external use
|
||||
export type { ConcreteApiPaths } from './apiPaths'
|
||||
|
||||
/**
|
||||
* Complete API Schema definitions for Test API
|
||||
*
|
||||
* Each path defines the supported HTTP methods with their:
|
||||
* - Request parameters (params, query, body)
|
||||
* - Response types
|
||||
* - Type safety guarantees
|
||||
*
|
||||
* This schema serves as the contract between renderer and main processes,
|
||||
* enabling full TypeScript type checking across IPC boundaries.
|
||||
*/
|
||||
export interface ApiSchemas {
|
||||
/**
|
||||
* Test items collection endpoint
|
||||
* @example GET /test/items?page=1&limit=10&search=hello
|
||||
* @example POST /test/items { "title": "New Test Item" }
|
||||
*/
|
||||
'/test/items': {
|
||||
/** List all test items with optional filtering and pagination */
|
||||
GET: {
|
||||
query?: PaginationParams & {
|
||||
/** Search items by title or description */
|
||||
search?: string
|
||||
/** Filter by item type */
|
||||
type?: string
|
||||
/** Filter by status */
|
||||
status?: string
|
||||
}
|
||||
response: PaginatedResponse<any>
|
||||
}
|
||||
/** Create a new test item */
|
||||
POST: {
|
||||
body: {
|
||||
title: string
|
||||
description?: string
|
||||
type?: string
|
||||
status?: string
|
||||
priority?: string
|
||||
tags?: string[]
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
response: any
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual test item endpoint
|
||||
* @example GET /test/items/123
|
||||
* @example PUT /test/items/123 { "title": "Updated Title" }
|
||||
* @example DELETE /test/items/123
|
||||
*/
|
||||
'/test/items/:id': {
|
||||
/** Get a specific test item by ID */
|
||||
GET: {
|
||||
params: { id: string }
|
||||
response: any
|
||||
}
|
||||
/** Update a specific test item */
|
||||
PUT: {
|
||||
params: { id: string }
|
||||
body: {
|
||||
title?: string
|
||||
description?: string
|
||||
type?: string
|
||||
status?: string
|
||||
priority?: string
|
||||
tags?: string[]
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
response: any
|
||||
}
|
||||
/** Delete a specific test item */
|
||||
DELETE: {
|
||||
params: { id: string }
|
||||
response: void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test search endpoint
|
||||
* @example GET /test/search?query=hello&page=1&limit=20
|
||||
*/
|
||||
'/test/search': {
|
||||
/** Search test items */
|
||||
GET: {
|
||||
query: {
|
||||
/** Search query string */
|
||||
query: string
|
||||
/** Page number for pagination */
|
||||
page?: number
|
||||
/** Number of results per page */
|
||||
limit?: number
|
||||
/** Additional filters */
|
||||
type?: string
|
||||
status?: string
|
||||
}
|
||||
response: PaginatedResponse<any>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test statistics endpoint
|
||||
* @example GET /test/stats
|
||||
*/
|
||||
'/test/stats': {
|
||||
/** Get comprehensive test statistics */
|
||||
GET: {
|
||||
response: {
|
||||
/** Total number of items */
|
||||
total: number
|
||||
/** Item count grouped by type */
|
||||
byType: Record<string, number>
|
||||
/** Item count grouped by status */
|
||||
byStatus: Record<string, number>
|
||||
/** Item count grouped by priority */
|
||||
byPriority: Record<string, number>
|
||||
/** Recent activity timeline */
|
||||
recentActivity: Array<{
|
||||
/** Date of activity */
|
||||
date: string
|
||||
/** Number of items on that date */
|
||||
count: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test bulk operations endpoint
|
||||
* @example POST /test/bulk { "operation": "create", "data": [...] }
|
||||
*/
|
||||
'/test/bulk': {
|
||||
/** Perform bulk operations on test items */
|
||||
POST: {
|
||||
body: {
|
||||
/** Operation type */
|
||||
operation: 'create' | 'update' | 'delete'
|
||||
/** Array of data items to process */
|
||||
data: any[]
|
||||
}
|
||||
response: {
|
||||
successful: number
|
||||
failed: number
|
||||
errors: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error simulation endpoint
|
||||
* @example POST /test/error { "errorType": "timeout" }
|
||||
*/
|
||||
'/test/error': {
|
||||
/** Simulate various error scenarios for testing */
|
||||
POST: {
|
||||
body: {
|
||||
/** Type of error to simulate */
|
||||
errorType:
|
||||
| 'timeout'
|
||||
| 'network'
|
||||
| 'server'
|
||||
| 'notfound'
|
||||
| 'validation'
|
||||
| 'unauthorized'
|
||||
| 'ratelimit'
|
||||
| 'generic'
|
||||
}
|
||||
response: never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test slow response endpoint
|
||||
* @example POST /test/slow { "delay": 2000 }
|
||||
*/
|
||||
'/test/slow': {
|
||||
/** Test slow response for performance testing */
|
||||
POST: {
|
||||
body: {
|
||||
/** Delay in milliseconds */
|
||||
delay: number
|
||||
}
|
||||
response: {
|
||||
message: string
|
||||
delay: number
|
||||
timestamp: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test data reset endpoint
|
||||
* @example POST /test/reset
|
||||
*/
|
||||
'/test/reset': {
|
||||
/** Reset all test data to initial state */
|
||||
POST: {
|
||||
response: {
|
||||
message: string
|
||||
timestamp: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test config endpoint
|
||||
* @example GET /test/config
|
||||
* @example PUT /test/config { "setting": "value" }
|
||||
*/
|
||||
'/test/config': {
|
||||
/** Get test configuration */
|
||||
GET: {
|
||||
response: Record<string, any>
|
||||
}
|
||||
/** Update test configuration */
|
||||
PUT: {
|
||||
body: Record<string, any>
|
||||
response: Record<string, any>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test status endpoint
|
||||
* @example GET /test/status
|
||||
*/
|
||||
'/test/status': {
|
||||
/** Get system test status */
|
||||
GET: {
|
||||
response: {
|
||||
status: string
|
||||
timestamp: string
|
||||
version: string
|
||||
uptime: number
|
||||
environment: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test performance endpoint
|
||||
* @example GET /test/performance
|
||||
*/
|
||||
'/test/performance': {
|
||||
/** Get performance metrics */
|
||||
GET: {
|
||||
response: {
|
||||
requestsPerSecond: number
|
||||
averageLatency: number
|
||||
memoryUsage: number
|
||||
cpuUsage: number
|
||||
uptime: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch execution of multiple requests
|
||||
* @example POST /batch { "requests": [...], "parallel": true }
|
||||
*/
|
||||
'/batch': {
|
||||
/** Execute multiple API requests in a single call */
|
||||
POST: {
|
||||
body: {
|
||||
/** Array of requests to execute */
|
||||
requests: Array<{
|
||||
/** HTTP method for the request */
|
||||
method: HttpMethod
|
||||
/** API path for the request */
|
||||
path: string
|
||||
/** URL parameters */
|
||||
params?: any
|
||||
/** Request body */
|
||||
body?: any
|
||||
}>
|
||||
/** Execute requests in parallel vs sequential */
|
||||
parallel?: boolean
|
||||
}
|
||||
response: {
|
||||
/** Results array matching input order */
|
||||
results: Array<{
|
||||
/** HTTP status code */
|
||||
status: number
|
||||
/** Response data if successful */
|
||||
data?: any
|
||||
/** Error information if failed */
|
||||
error?: any
|
||||
}>
|
||||
/** Batch execution metadata */
|
||||
metadata: {
|
||||
/** Total execution duration in ms */
|
||||
duration: number
|
||||
/** Number of successful requests */
|
||||
successCount: number
|
||||
/** Number of failed requests */
|
||||
errorCount: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomic transaction of multiple operations
|
||||
* @example POST /transaction { "operations": [...], "options": { "rollbackOnError": true } }
|
||||
*/
|
||||
'/transaction': {
|
||||
/** Execute multiple operations in a database transaction */
|
||||
POST: {
|
||||
body: {
|
||||
/** Array of operations to execute atomically */
|
||||
operations: Array<{
|
||||
/** HTTP method for the operation */
|
||||
method: HttpMethod
|
||||
/** API path for the operation */
|
||||
path: string
|
||||
/** URL parameters */
|
||||
params?: any
|
||||
/** Request body */
|
||||
body?: any
|
||||
}>
|
||||
/** Transaction configuration options */
|
||||
options?: {
|
||||
/** Database isolation level */
|
||||
isolation?: 'read-uncommitted' | 'read-committed' | 'repeatable-read' | 'serializable'
|
||||
/** Rollback all operations on any error */
|
||||
rollbackOnError?: boolean
|
||||
/** Transaction timeout in milliseconds */
|
||||
timeout?: number
|
||||
}
|
||||
}
|
||||
response: Array<{
|
||||
/** HTTP status code */
|
||||
status: number
|
||||
/** Response data if successful */
|
||||
data?: any
|
||||
/** Error information if failed */
|
||||
error?: any
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified type extraction helpers
|
||||
*/
|
||||
export type ApiPaths = keyof ApiSchemas
|
||||
export type ApiMethods<TPath extends ApiPaths> = keyof ApiSchemas[TPath] & HttpMethod
|
||||
export type ApiResponse<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { response: infer R }
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
export type ApiParams<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { params: infer P }
|
||||
? P
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
export type ApiQuery<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { query: infer Q }
|
||||
? Q
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
export type ApiBody<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { body: infer B }
|
||||
? B
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
/**
|
||||
* Type-safe API client interface using concrete paths
|
||||
* Accepts actual paths like '/test/items/123' instead of '/test/items/:id'
|
||||
* Automatically infers query, body, and response types from ApiSchemas
|
||||
*/
|
||||
export interface ApiClient {
|
||||
get<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options?: {
|
||||
query?: QueryParamsForPath<TPath>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'GET'>>
|
||||
|
||||
post<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body?: BodyForPath<TPath, 'POST'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'POST'>>
|
||||
|
||||
put<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body: BodyForPath<TPath, 'PUT'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'PUT'>>
|
||||
|
||||
delete<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options?: {
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'DELETE'>>
|
||||
|
||||
patch<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body?: BodyForPath<TPath, 'PATCH'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'PATCH'>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper types to determine if parameters are required based on schema
|
||||
*/
|
||||
type HasRequiredQuery<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { query: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
type HasRequiredBody<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { body: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
type HasRequiredParams<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { params: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
/**
|
||||
* Handler function for a specific API endpoint
|
||||
* Provides type-safe parameter extraction based on ApiSchemas
|
||||
* Parameters are required or optional based on the schema definition
|
||||
*/
|
||||
export type ApiHandler<Path extends ApiPaths, Method extends ApiMethods<Path>> = (
|
||||
params: (HasRequiredParams<Path, Method> extends true
|
||||
? { params: ApiParams<Path, Method> }
|
||||
: { params?: ApiParams<Path, Method> }) &
|
||||
(HasRequiredQuery<Path, Method> extends true
|
||||
? { query: ApiQuery<Path, Method> }
|
||||
: { query?: ApiQuery<Path, Method> }) &
|
||||
(HasRequiredBody<Path, Method> extends true ? { body: ApiBody<Path, Method> } : { body?: ApiBody<Path, Method> })
|
||||
) => Promise<ApiResponse<Path, Method>>
|
||||
|
||||
/**
|
||||
* Complete API implementation that must match ApiSchemas structure
|
||||
* TypeScript will error if any endpoint is missing - this ensures exhaustive coverage
|
||||
*/
|
||||
export type ApiImplementation = {
|
||||
[Path in ApiPaths]: {
|
||||
[Method in ApiMethods<Path>]: ApiHandler<Path, Method>
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,75 @@
|
||||
*/
|
||||
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
||||
|
||||
// ============================================================================
|
||||
// Schema Constraint Types
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Constraint for a single endpoint method definition.
|
||||
* Requires `response` field, allows optional `params`, `query`, and `body`.
|
||||
*/
|
||||
export type EndpointMethodConstraint = {
|
||||
params?: Record<string, any>
|
||||
query?: Record<string, any>
|
||||
body?: any
|
||||
response: any // response is required
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for a single API path - only allows valid HTTP methods.
|
||||
*/
|
||||
export type EndpointConstraint = {
|
||||
[Method in HttpMethod]?: EndpointMethodConstraint
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a schema only contains valid HTTP methods.
|
||||
* Used in AssertValidSchemas for compile-time validation.
|
||||
*/
|
||||
type ValidateMethods<T> = {
|
||||
[Path in keyof T]: {
|
||||
[Method in keyof T[Path]]: Method extends HttpMethod ? T[Path][Method] : never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all endpoints have a `response` field.
|
||||
* Returns the original type if valid, or `never` if any endpoint lacks response.
|
||||
*/
|
||||
type ValidateResponses<T> = {
|
||||
[Path in keyof T]: {
|
||||
[Method in keyof T[Path]]: T[Path][Method] extends { response: any }
|
||||
? T[Path][Method]
|
||||
: { error: `Endpoint ${Path & string}.${Method & string} is missing 'response' field` }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a schema conforms to expected structure:
|
||||
* 1. All methods must be valid HTTP methods (GET, POST, PUT, DELETE, PATCH)
|
||||
* 2. All endpoints must have a `response` field
|
||||
*
|
||||
* This is applied at the composition level (schemas/index.ts) to catch
|
||||
* invalid schemas even if individual schema files don't use validation.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In schemas/index.ts
|
||||
* export type ApiSchemas = AssertValidSchemas<TestSchemas & BatchSchemas>
|
||||
*
|
||||
* // Invalid method will cause error:
|
||||
* // Type 'never' is not assignable to type...
|
||||
* ```
|
||||
*/
|
||||
export type AssertValidSchemas<T> = ValidateMethods<T> & ValidateResponses<T> extends infer R
|
||||
? { [K in keyof R]: R[K] }
|
||||
: never
|
||||
|
||||
// ============================================================================
|
||||
// Core Request/Response Types
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Request object structure for Data API calls
|
||||
*/
|
||||
@ -30,8 +99,6 @@ export interface DataRequest<T = any> {
|
||||
timestamp: number
|
||||
/** OpenTelemetry span context for tracing */
|
||||
spanContext?: any
|
||||
/** Cache options for this specific request */
|
||||
cache?: CacheOptions
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,22 +168,6 @@ export enum ErrorCode {
|
||||
CONCURRENT_MODIFICATION = 'CONCURRENT_MODIFICATION'
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache configuration options
|
||||
*/
|
||||
export interface CacheOptions {
|
||||
/** Cache TTL in seconds */
|
||||
ttl?: number
|
||||
/** Return stale data while revalidating in background */
|
||||
staleWhileRevalidate?: boolean
|
||||
/** Custom cache key override */
|
||||
cacheKey?: string
|
||||
/** Operations that should invalidate this cache entry */
|
||||
invalidateOn?: string[]
|
||||
/** Whether to bypass cache entirely */
|
||||
noCache?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction request wrapper for atomic operations
|
||||
*/
|
||||
@ -274,16 +325,169 @@ export interface ServiceOptions {
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Schema Type Utilities
|
||||
// ============================================================================
|
||||
|
||||
import type { BodyForPath, ConcreteApiPaths, QueryParamsForPath, ResponseForPath } from './apiPaths'
|
||||
import type { ApiSchemas } from './schemas'
|
||||
|
||||
// Re-export for external use
|
||||
export type { ConcreteApiPaths } from './apiPaths'
|
||||
export type { ApiSchemas } from './schemas'
|
||||
|
||||
/**
|
||||
* Standard service response wrapper
|
||||
* All available API paths
|
||||
*/
|
||||
export interface ServiceResult<T = any> {
|
||||
/** Whether operation was successful */
|
||||
success: boolean
|
||||
/** Result data if successful */
|
||||
data?: T
|
||||
/** Error information if failed */
|
||||
error?: DataApiError
|
||||
/** Additional metadata */
|
||||
metadata?: Record<string, any>
|
||||
export type ApiPaths = keyof ApiSchemas
|
||||
|
||||
/**
|
||||
* Available HTTP methods for a specific path
|
||||
*/
|
||||
export type ApiMethods<TPath extends ApiPaths> = keyof ApiSchemas[TPath] & HttpMethod
|
||||
|
||||
/**
|
||||
* Response type for a specific path and method
|
||||
*/
|
||||
export type ApiResponse<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { response: infer R }
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
/**
|
||||
* URL params type for a specific path and method
|
||||
*/
|
||||
export type ApiParams<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { params: infer P }
|
||||
? P
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
/**
|
||||
* Query params type for a specific path and method
|
||||
*/
|
||||
export type ApiQuery<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { query: infer Q }
|
||||
? Q
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
/**
|
||||
* Request body type for a specific path and method
|
||||
*/
|
||||
export type ApiBody<TPath extends ApiPaths, TMethod extends string> = TPath extends keyof ApiSchemas
|
||||
? TMethod extends keyof ApiSchemas[TPath]
|
||||
? ApiSchemas[TPath][TMethod] extends { body: infer B }
|
||||
? B
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
|
||||
/**
|
||||
* Type-safe API client interface using concrete paths
|
||||
* Accepts actual paths like '/test/items/123' instead of '/test/items/:id'
|
||||
* Automatically infers query, body, and response types from ApiSchemas
|
||||
*/
|
||||
export interface ApiClient {
|
||||
get<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options?: {
|
||||
query?: QueryParamsForPath<TPath>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'GET'>>
|
||||
|
||||
post<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body?: BodyForPath<TPath, 'POST'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'POST'>>
|
||||
|
||||
put<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body: BodyForPath<TPath, 'PUT'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'PUT'>>
|
||||
|
||||
delete<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options?: {
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'DELETE'>>
|
||||
|
||||
patch<TPath extends ConcreteApiPaths>(
|
||||
path: TPath,
|
||||
options: {
|
||||
body?: BodyForPath<TPath, 'PATCH'>
|
||||
query?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ResponseForPath<TPath, 'PATCH'>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper types to determine if parameters are required based on schema
|
||||
*/
|
||||
type HasRequiredQuery<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { query: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
type HasRequiredBody<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { body: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
type HasRequiredParams<Path extends ApiPaths, Method extends ApiMethods<Path>> = Path extends keyof ApiSchemas
|
||||
? Method extends keyof ApiSchemas[Path]
|
||||
? ApiSchemas[Path][Method] extends { params: any }
|
||||
? true
|
||||
: false
|
||||
: false
|
||||
: false
|
||||
|
||||
/**
|
||||
* Handler function for a specific API endpoint
|
||||
* Provides type-safe parameter extraction based on ApiSchemas
|
||||
* Parameters are required or optional based on the schema definition
|
||||
*/
|
||||
export type ApiHandler<Path extends ApiPaths, Method extends ApiMethods<Path>> = (
|
||||
params: (HasRequiredParams<Path, Method> extends true
|
||||
? { params: ApiParams<Path, Method> }
|
||||
: { params?: ApiParams<Path, Method> }) &
|
||||
(HasRequiredQuery<Path, Method> extends true
|
||||
? { query: ApiQuery<Path, Method> }
|
||||
: { query?: ApiQuery<Path, Method> }) &
|
||||
(HasRequiredBody<Path, Method> extends true ? { body: ApiBody<Path, Method> } : { body?: ApiBody<Path, Method> })
|
||||
) => Promise<ApiResponse<Path, Method>>
|
||||
|
||||
/**
|
||||
* Complete API implementation that must match ApiSchemas structure
|
||||
* TypeScript will error if any endpoint is missing - this ensures exhaustive coverage
|
||||
*/
|
||||
export type ApiImplementation = {
|
||||
[Path in ApiPaths]: {
|
||||
[Method in ApiMethods<Path>]: ApiHandler<Path, Method>
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,70 +1,71 @@
|
||||
/**
|
||||
* Cherry Studio Data API - Barrel Exports
|
||||
*
|
||||
* This file provides a centralized entry point for all data API types,
|
||||
* schemas, and utilities. Import everything you need from this single location.
|
||||
* Exports common infrastructure types for the Data API system.
|
||||
* Domain-specific DTOs should be imported directly from their schema files.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { Topic, CreateTopicDto, ApiSchemas, DataRequest, ErrorCode } from '@/shared/data'
|
||||
* // Infrastructure types from barrel export
|
||||
* import { DataRequest, DataResponse, ErrorCode, ApiClient } from '@shared/data/api'
|
||||
*
|
||||
* // Domain DTOs from schema files directly
|
||||
* import type { Topic, CreateTopicDto } from '@shared/data/api/schemas/topic'
|
||||
* ```
|
||||
*/
|
||||
|
||||
// Core data API types and infrastructure
|
||||
// ============================================================================
|
||||
// Core Request/Response Types
|
||||
// ============================================================================
|
||||
|
||||
export type {
|
||||
BatchRequest,
|
||||
BatchResponse,
|
||||
CacheOptions,
|
||||
DataApiError,
|
||||
DataRequest,
|
||||
DataResponse,
|
||||
HttpMethod,
|
||||
Middleware,
|
||||
PaginatedResponse,
|
||||
PaginationParams,
|
||||
RequestContext,
|
||||
ServiceOptions,
|
||||
ServiceResult,
|
||||
SubscriptionCallback,
|
||||
SubscriptionOptions,
|
||||
TransactionRequest
|
||||
} from './apiTypes'
|
||||
export { ErrorCode, SubscriptionEvent } from './apiTypes'
|
||||
|
||||
// Domain models and DTOs
|
||||
export type {
|
||||
BulkOperationRequest,
|
||||
BulkOperationResponse,
|
||||
CreateTestItemDto,
|
||||
TestItem,
|
||||
UpdateTestItemDto
|
||||
} from './apiModels'
|
||||
// ============================================================================
|
||||
// API Schema Type Utilities
|
||||
// ============================================================================
|
||||
|
||||
// API schema definitions and type helpers
|
||||
export type {
|
||||
ApiBody,
|
||||
ApiClient,
|
||||
ApiHandler,
|
||||
ApiImplementation,
|
||||
ApiMethods,
|
||||
ApiParams,
|
||||
ApiPaths,
|
||||
ApiQuery,
|
||||
ApiResponse,
|
||||
ApiSchemas
|
||||
} from './apiSchemas'
|
||||
ApiSchemas,
|
||||
ConcreteApiPaths
|
||||
} from './apiTypes'
|
||||
|
||||
// ============================================================================
|
||||
// Path Resolution Utilities
|
||||
// ============================================================================
|
||||
|
||||
// Path type utilities for template literal types
|
||||
export type {
|
||||
BodyForPath,
|
||||
ConcreteApiPaths,
|
||||
MatchApiPath,
|
||||
QueryParamsForPath,
|
||||
ResolvedPath,
|
||||
ResponseForPath
|
||||
} from './apiPaths'
|
||||
|
||||
// Error handling utilities
|
||||
// ============================================================================
|
||||
// Error Handling
|
||||
// ============================================================================
|
||||
|
||||
export { ErrorCode, SubscriptionEvent } from './apiTypes'
|
||||
export {
|
||||
ErrorCode as DataApiErrorCode,
|
||||
DataApiErrorFactory,
|
||||
ERROR_MESSAGES,
|
||||
ERROR_STATUS_MAP,
|
||||
@ -72,50 +73,14 @@ export {
|
||||
toDataApiError
|
||||
} from './errorCodes'
|
||||
|
||||
/**
|
||||
* Re-export commonly used type combinations for convenience
|
||||
*/
|
||||
// ============================================================================
|
||||
// Subscription & Middleware (for advanced usage)
|
||||
// ============================================================================
|
||||
|
||||
// Import types for re-export convenience types
|
||||
import type { CreateTestItemDto, TestItem, UpdateTestItemDto } from './apiModels'
|
||||
import type {
|
||||
BatchRequest,
|
||||
BatchResponse,
|
||||
DataApiError,
|
||||
DataRequest,
|
||||
DataResponse,
|
||||
ErrorCode,
|
||||
PaginatedResponse,
|
||||
PaginationParams,
|
||||
TransactionRequest
|
||||
export type {
|
||||
Middleware,
|
||||
RequestContext,
|
||||
ServiceOptions,
|
||||
SubscriptionCallback,
|
||||
SubscriptionOptions
|
||||
} from './apiTypes'
|
||||
import type { DataApiErrorFactory } from './errorCodes'
|
||||
|
||||
/** All test item-related types */
|
||||
export type TestItemTypes = {
|
||||
TestItem: TestItem
|
||||
CreateTestItemDto: CreateTestItemDto
|
||||
UpdateTestItemDto: UpdateTestItemDto
|
||||
}
|
||||
|
||||
/** All error-related types and utilities */
|
||||
export type ErrorTypes = {
|
||||
DataApiError: DataApiError
|
||||
ErrorCode: ErrorCode
|
||||
ErrorFactory: typeof DataApiErrorFactory
|
||||
}
|
||||
|
||||
/** All request/response types */
|
||||
export type RequestTypes = {
|
||||
DataRequest: DataRequest
|
||||
DataResponse: DataResponse
|
||||
BatchRequest: BatchRequest
|
||||
BatchResponse: BatchResponse
|
||||
TransactionRequest: TransactionRequest
|
||||
}
|
||||
|
||||
/** All pagination-related types */
|
||||
export type PaginationTypes = {
|
||||
PaginationParams: PaginationParams
|
||||
PaginatedResponse: PaginatedResponse<any>
|
||||
}
|
||||
|
||||
140
packages/shared/data/api/schemas/batch.ts
Normal file
140
packages/shared/data/api/schemas/batch.ts
Normal file
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Batch and Transaction API Schema definitions
|
||||
*
|
||||
* Contains cross-domain operations for batch processing and atomic transactions.
|
||||
* These endpoints are domain-agnostic and work with any API path.
|
||||
*/
|
||||
|
||||
import type { HttpMethod } from '../apiTypes'
|
||||
|
||||
// ============================================================================
|
||||
// Domain Models & DTOs
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Request for bulk operations on multiple items
|
||||
*/
|
||||
export interface BulkOperationRequest<TData = any> {
|
||||
/** Type of bulk operation to perform */
|
||||
operation: 'create' | 'update' | 'delete' | 'archive' | 'restore'
|
||||
/** Array of data items to process */
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from a bulk operation
|
||||
*/
|
||||
export interface BulkOperationResponse {
|
||||
/** Number of successfully processed items */
|
||||
successful: number
|
||||
/** Number of items that failed processing */
|
||||
failed: number
|
||||
/** Array of errors that occurred during processing */
|
||||
errors: Array<{
|
||||
/** Index of the item that failed */
|
||||
index: number
|
||||
/** Error message */
|
||||
error: string
|
||||
/** Optional additional error data */
|
||||
data?: any
|
||||
}>
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Schema Definitions
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Batch and Transaction API Schema definitions
|
||||
*
|
||||
* Validation is performed at composition level via AssertValidSchemas
|
||||
* in schemas/index.ts, which ensures:
|
||||
* - All methods are valid HTTP methods (GET, POST, PUT, DELETE, PATCH)
|
||||
* - All endpoints have a `response` field
|
||||
*/
|
||||
export interface BatchSchemas {
|
||||
/**
|
||||
* Batch execution of multiple requests
|
||||
* @example POST /batch { "requests": [...], "parallel": true }
|
||||
*/
|
||||
'/batch': {
|
||||
/** Execute multiple API requests in a single call */
|
||||
POST: {
|
||||
body: {
|
||||
/** Array of requests to execute */
|
||||
requests: Array<{
|
||||
/** HTTP method for the request */
|
||||
method: HttpMethod
|
||||
/** API path for the request */
|
||||
path: string
|
||||
/** URL parameters */
|
||||
params?: any
|
||||
/** Request body */
|
||||
body?: any
|
||||
}>
|
||||
/** Execute requests in parallel vs sequential */
|
||||
parallel?: boolean
|
||||
}
|
||||
response: {
|
||||
/** Results array matching input order */
|
||||
results: Array<{
|
||||
/** HTTP status code */
|
||||
status: number
|
||||
/** Response data if successful */
|
||||
data?: any
|
||||
/** Error information if failed */
|
||||
error?: any
|
||||
}>
|
||||
/** Batch execution metadata */
|
||||
metadata: {
|
||||
/** Total execution duration in ms */
|
||||
duration: number
|
||||
/** Number of successful requests */
|
||||
successCount: number
|
||||
/** Number of failed requests */
|
||||
errorCount: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomic transaction of multiple operations
|
||||
* @example POST /transaction { "operations": [...], "options": { "rollbackOnError": true } }
|
||||
*/
|
||||
'/transaction': {
|
||||
/** Execute multiple operations in a database transaction */
|
||||
POST: {
|
||||
body: {
|
||||
/** Array of operations to execute atomically */
|
||||
operations: Array<{
|
||||
/** HTTP method for the operation */
|
||||
method: HttpMethod
|
||||
/** API path for the operation */
|
||||
path: string
|
||||
/** URL parameters */
|
||||
params?: any
|
||||
/** Request body */
|
||||
body?: any
|
||||
}>
|
||||
/** Transaction configuration options */
|
||||
options?: {
|
||||
/** Database isolation level */
|
||||
isolation?: 'read-uncommitted' | 'read-committed' | 'repeatable-read' | 'serializable'
|
||||
/** Rollback all operations on any error */
|
||||
rollbackOnError?: boolean
|
||||
/** Transaction timeout in milliseconds */
|
||||
timeout?: number
|
||||
}
|
||||
}
|
||||
response: Array<{
|
||||
/** HTTP status code */
|
||||
status: number
|
||||
/** Response data if successful */
|
||||
data?: any
|
||||
/** Error information if failed */
|
||||
error?: any
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
43
packages/shared/data/api/schemas/index.ts
Normal file
43
packages/shared/data/api/schemas/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Schema Index - Composes all domain schemas into unified ApiSchemas
|
||||
*
|
||||
* This file has ONE responsibility: compose domain schemas into ApiSchemas.
|
||||
*
|
||||
* Import conventions (see api/README.md for details):
|
||||
* - Infrastructure types: import from '@shared/data/api'
|
||||
* - Domain DTOs: import directly from schema files (e.g., '@shared/data/api/schemas/topic')
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Infrastructure types via barrel export
|
||||
* import type { ApiSchemas, DataRequest } from '@shared/data/api'
|
||||
*
|
||||
* // Domain DTOs directly from schema files
|
||||
* import type { TestItem, CreateTestItemDto } from '@shared/data/api/schemas/test'
|
||||
* import type { Topic, CreateTopicDto } from '@shared/data/api/schemas/topic'
|
||||
* ```
|
||||
*/
|
||||
|
||||
import type { AssertValidSchemas } from '../apiTypes'
|
||||
import type { BatchSchemas } from './batch'
|
||||
import type { TestSchemas } from './test'
|
||||
|
||||
/**
|
||||
* Merged API Schemas - single source of truth for all API endpoints
|
||||
*
|
||||
* All domain schemas are composed here using intersection types.
|
||||
* AssertValidSchemas provides compile-time validation:
|
||||
* - Invalid HTTP methods become `never` type
|
||||
* - Missing `response` field causes type errors
|
||||
*
|
||||
* When adding a new domain:
|
||||
* 1. Create the schema file (e.g., topic.ts)
|
||||
* 2. Import and add to intersection below
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import type { TopicSchemas } from './topic'
|
||||
* export type ApiSchemas = AssertValidSchemas<TestSchemas & BatchSchemas & TopicSchemas>
|
||||
* ```
|
||||
*/
|
||||
export type ApiSchemas = AssertValidSchemas<TestSchemas & BatchSchemas>
|
||||
322
packages/shared/data/api/schemas/test.ts
Normal file
322
packages/shared/data/api/schemas/test.ts
Normal file
@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Test API Schema definitions
|
||||
*
|
||||
* Contains all test-related endpoints for development and testing purposes.
|
||||
* These endpoints demonstrate the API patterns and provide testing utilities.
|
||||
*/
|
||||
|
||||
import type { PaginatedResponse, PaginationParams } from '../apiTypes'
|
||||
|
||||
// ============================================================================
|
||||
// Domain Models & DTOs
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generic test item entity - flexible structure for testing various scenarios
|
||||
*/
|
||||
export interface TestItem {
|
||||
/** Unique identifier */
|
||||
id: string
|
||||
/** Item title */
|
||||
title: string
|
||||
/** Optional description */
|
||||
description?: string
|
||||
/** Type category */
|
||||
type: string
|
||||
/** Current status */
|
||||
status: string
|
||||
/** Priority level */
|
||||
priority: string
|
||||
/** Associated tags */
|
||||
tags: string[]
|
||||
/** Creation timestamp */
|
||||
createdAt: string
|
||||
/** Last update timestamp */
|
||||
updatedAt: string
|
||||
/** Additional metadata */
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO for creating a new test item
|
||||
*/
|
||||
export interface CreateTestItemDto {
|
||||
/** Item title */
|
||||
title: string
|
||||
/** Optional description */
|
||||
description?: string
|
||||
/** Type category */
|
||||
type?: string
|
||||
/** Current status */
|
||||
status?: string
|
||||
/** Priority level */
|
||||
priority?: string
|
||||
/** Associated tags */
|
||||
tags?: string[]
|
||||
/** Additional metadata */
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO for updating an existing test item
|
||||
*/
|
||||
export interface UpdateTestItemDto {
|
||||
/** Updated title */
|
||||
title?: string
|
||||
/** Updated description */
|
||||
description?: string
|
||||
/** Updated type */
|
||||
type?: string
|
||||
/** Updated status */
|
||||
status?: string
|
||||
/** Updated priority */
|
||||
priority?: string
|
||||
/** Updated tags */
|
||||
tags?: string[]
|
||||
/** Updated metadata */
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Schema Definitions
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Test API Schema definitions
|
||||
*
|
||||
* Validation is performed at composition level via AssertValidSchemas
|
||||
* in schemas/index.ts, which ensures:
|
||||
* - All methods are valid HTTP methods (GET, POST, PUT, DELETE, PATCH)
|
||||
* - All endpoints have a `response` field
|
||||
*/
|
||||
export interface TestSchemas {
|
||||
/**
|
||||
* Test items collection endpoint
|
||||
* @example GET /test/items?page=1&limit=10&search=hello
|
||||
* @example POST /test/items { "title": "New Test Item" }
|
||||
*/
|
||||
'/test/items': {
|
||||
/** List all test items with optional filtering and pagination */
|
||||
GET: {
|
||||
query?: PaginationParams & {
|
||||
/** Search items by title or description */
|
||||
search?: string
|
||||
/** Filter by item type */
|
||||
type?: string
|
||||
/** Filter by status */
|
||||
status?: string
|
||||
}
|
||||
response: PaginatedResponse<TestItem>
|
||||
}
|
||||
/** Create a new test item */
|
||||
POST: {
|
||||
body: CreateTestItemDto
|
||||
response: TestItem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual test item endpoint
|
||||
* @example GET /test/items/123
|
||||
* @example PUT /test/items/123 { "title": "Updated Title" }
|
||||
* @example DELETE /test/items/123
|
||||
*/
|
||||
'/test/items/:id': {
|
||||
/** Get a specific test item by ID */
|
||||
GET: {
|
||||
params: { id: string }
|
||||
response: TestItem
|
||||
}
|
||||
/** Update a specific test item */
|
||||
PUT: {
|
||||
params: { id: string }
|
||||
body: UpdateTestItemDto
|
||||
response: TestItem
|
||||
}
|
||||
/** Delete a specific test item */
|
||||
DELETE: {
|
||||
params: { id: string }
|
||||
response: void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test search endpoint
|
||||
* @example GET /test/search?query=hello&page=1&limit=20
|
||||
*/
|
||||
'/test/search': {
|
||||
/** Search test items */
|
||||
GET: {
|
||||
query: {
|
||||
/** Search query string */
|
||||
query: string
|
||||
/** Page number for pagination */
|
||||
page?: number
|
||||
/** Number of results per page */
|
||||
limit?: number
|
||||
/** Additional filters */
|
||||
type?: string
|
||||
status?: string
|
||||
}
|
||||
response: PaginatedResponse<TestItem>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test statistics endpoint
|
||||
* @example GET /test/stats
|
||||
*/
|
||||
'/test/stats': {
|
||||
/** Get comprehensive test statistics */
|
||||
GET: {
|
||||
response: {
|
||||
/** Total number of items */
|
||||
total: number
|
||||
/** Item count grouped by type */
|
||||
byType: Record<string, number>
|
||||
/** Item count grouped by status */
|
||||
byStatus: Record<string, number>
|
||||
/** Item count grouped by priority */
|
||||
byPriority: Record<string, number>
|
||||
/** Recent activity timeline */
|
||||
recentActivity: Array<{
|
||||
/** Date of activity */
|
||||
date: string
|
||||
/** Number of items on that date */
|
||||
count: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test bulk operations endpoint
|
||||
* @example POST /test/bulk { "operation": "create", "data": [...] }
|
||||
*/
|
||||
'/test/bulk': {
|
||||
/** Perform bulk operations on test items */
|
||||
POST: {
|
||||
body: {
|
||||
/** Operation type */
|
||||
operation: 'create' | 'update' | 'delete'
|
||||
/** Array of data items to process */
|
||||
data: Array<CreateTestItemDto | UpdateTestItemDto | string>
|
||||
}
|
||||
response: {
|
||||
/** Number of successfully processed items */
|
||||
successful: number
|
||||
/** Number of items that failed processing */
|
||||
failed: number
|
||||
/** Array of error messages */
|
||||
errors: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error simulation endpoint
|
||||
* @example POST /test/error { "errorType": "timeout" }
|
||||
*/
|
||||
'/test/error': {
|
||||
/** Simulate various error scenarios for testing */
|
||||
POST: {
|
||||
body: {
|
||||
/** Type of error to simulate */
|
||||
errorType:
|
||||
| 'timeout'
|
||||
| 'network'
|
||||
| 'server'
|
||||
| 'notfound'
|
||||
| 'validation'
|
||||
| 'unauthorized'
|
||||
| 'ratelimit'
|
||||
| 'generic'
|
||||
}
|
||||
response: never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test slow response endpoint
|
||||
* @example POST /test/slow { "delay": 2000 }
|
||||
*/
|
||||
'/test/slow': {
|
||||
/** Test slow response for performance testing */
|
||||
POST: {
|
||||
body: {
|
||||
/** Delay in milliseconds */
|
||||
delay: number
|
||||
}
|
||||
response: {
|
||||
message: string
|
||||
delay: number
|
||||
timestamp: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test data reset endpoint
|
||||
* @example POST /test/reset
|
||||
*/
|
||||
'/test/reset': {
|
||||
/** Reset all test data to initial state */
|
||||
POST: {
|
||||
response: {
|
||||
message: string
|
||||
timestamp: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test config endpoint
|
||||
* @example GET /test/config
|
||||
* @example PUT /test/config { "setting": "value" }
|
||||
*/
|
||||
'/test/config': {
|
||||
/** Get test configuration */
|
||||
GET: {
|
||||
response: Record<string, any>
|
||||
}
|
||||
/** Update test configuration */
|
||||
PUT: {
|
||||
body: Record<string, any>
|
||||
response: Record<string, any>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test status endpoint
|
||||
* @example GET /test/status
|
||||
*/
|
||||
'/test/status': {
|
||||
/** Get system test status */
|
||||
GET: {
|
||||
response: {
|
||||
status: string
|
||||
timestamp: string
|
||||
version: string
|
||||
uptime: number
|
||||
environment: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test performance endpoint
|
||||
* @example GET /test/performance
|
||||
*/
|
||||
'/test/performance': {
|
||||
/** Get performance metrics */
|
||||
GET: {
|
||||
response: {
|
||||
requestsPerSecond: number
|
||||
averageLatency: number
|
||||
memoryUsage: number
|
||||
cpuUsage: number
|
||||
uptime: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,10 @@ src/main/data/
|
||||
│ │ ├── MiddlewareEngine.ts # Request/response middleware
|
||||
│ │ └── adapters/ # Communication adapters (IPC)
|
||||
│ ├── handlers/ # API endpoint implementations
|
||||
│ │ └── index.ts # Thin handlers: param extraction, DTO conversion
|
||||
│ └── index.ts # API framework exports
|
||||
│ │ ├── index.ts # Handler aggregation and exports
|
||||
│ │ ├── test.ts # Test endpoint handlers
|
||||
│ │ └── batch.ts # Batch/transaction handlers
|
||||
│ └── index.ts # API framework exports
|
||||
│
|
||||
├── services/ # Business logic layer
|
||||
│ ├── base/ # Service base classes and interfaces
|
||||
@ -34,6 +36,12 @@ src/main/data/
|
||||
│ ├── schemas/ # Drizzle table definitions
|
||||
│ │ ├── preference.ts # Preference configuration table
|
||||
│ │ ├── appState.ts # Application state table
|
||||
│ │ ├── topic.ts # Topic/conversation table
|
||||
│ │ ├── message.ts # Message table
|
||||
│ │ ├── group.ts # Group table
|
||||
│ │ ├── tag.ts # Tag table
|
||||
│ │ ├── entityTag.ts # Entity-tag relationship table
|
||||
│ │ ├── messageFts.ts # Message full-text search table
|
||||
│ │ └── columnHelpers.ts # Reusable column definitions
|
||||
│ ├── seeding/ # Database initialization
|
||||
│ └── DbService.ts # Database connection and management
|
||||
@ -94,8 +102,8 @@ The API framework provides the interface layer for data access:
|
||||
- Delegating to business services
|
||||
- Transforming responses for IPC
|
||||
- **Anti-pattern**: Do NOT put business logic in handlers
|
||||
- **Currently**: Contains test handlers (production handlers pending)
|
||||
- **Type Safety**: Must implement all endpoints defined in `@shared/data/api`
|
||||
- **Currently**: Contains test and batch handlers (business handlers pending)
|
||||
- **Type Safety**: Must implement all endpoints defined in `@shared/data/api/schemas/`
|
||||
|
||||
### Business Logic Layer (`services/`)
|
||||
|
||||
@ -217,6 +225,12 @@ export class SimpleService extends BaseService {
|
||||
### Current Tables
|
||||
- `preference`: User configuration storage
|
||||
- `appState`: Application state persistence
|
||||
- `topic`: Conversation/topic storage
|
||||
- `message`: Message storage with full-text search
|
||||
- `group`: Group organization
|
||||
- `tag`: Tag definitions
|
||||
- `entityTag`: Entity-tag relationships
|
||||
- `messageFts`: Message full-text search index
|
||||
|
||||
## Usage Examples
|
||||
|
||||
@ -231,11 +245,12 @@ import { dataApiService } from '@/data/DataApiService'
|
||||
```
|
||||
|
||||
### Adding New API Endpoints
|
||||
1. Define endpoint in `@shared/data/api/apiSchemas.ts`
|
||||
2. Implement handler in `api/handlers/index.ts` (thin layer, delegate to service)
|
||||
3. Create business service in `services/` for domain logic
|
||||
4. Create repository in `repositories/` if domain is complex (optional)
|
||||
5. Add database schema in `db/schemas/` if required
|
||||
1. Create or update schema in `@shared/data/api/schemas/` (see `@shared/data/api/README.md`)
|
||||
2. Register schema in `@shared/data/api/schemas/index.ts`
|
||||
3. Implement handler in `api/handlers/` (thin layer, delegate to service)
|
||||
4. Create business service in `services/` for domain logic
|
||||
5. Create repository in `repositories/` if domain is complex (optional)
|
||||
6. Add database schema in `db/schemas/` if required
|
||||
|
||||
### Adding Database Tables
|
||||
1. Create schema in `db/schemas/{tableName}.ts`
|
||||
@ -271,11 +286,13 @@ export class ExampleService {
|
||||
}
|
||||
|
||||
// 3. Create handler: api/handlers/example.ts
|
||||
import { ExampleService } from '../../services/ExampleService'
|
||||
import { ExampleService } from '@data/services/ExampleService'
|
||||
|
||||
export const exampleHandlers = {
|
||||
'POST /examples': async ({ body }) => {
|
||||
return await ExampleService.getInstance().createExample(body)
|
||||
export const exampleHandlers: Partial<ApiImplementation> = {
|
||||
'/examples': {
|
||||
POST: async ({ body }) => {
|
||||
return await ExampleService.getInstance().createExample(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -294,9 +311,11 @@ export class SimpleService extends BaseService {
|
||||
}
|
||||
|
||||
// 2. Create handler: api/handlers/simple.ts
|
||||
export const simpleHandlers = {
|
||||
'GET /items/:id': async ({ params }) => {
|
||||
return await SimpleService.getInstance().getItem(params.id)
|
||||
export const simpleHandlers: Partial<ApiImplementation> = {
|
||||
'/items/:id': {
|
||||
GET: async ({ params }) => {
|
||||
return await SimpleService.getInstance().getItem(params.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { loggerService } from '@logger'
|
||||
import type { ApiImplementation } from '@shared/data/api/apiSchemas'
|
||||
import type { ApiImplementation } from '@shared/data/api/apiTypes'
|
||||
import type { DataRequest, DataResponse, HttpMethod, RequestContext } from '@shared/data/api/apiTypes'
|
||||
import { DataApiErrorFactory, ErrorCode } from '@shared/data/api/errorCodes'
|
||||
|
||||
|
||||
55
src/main/data/api/handlers/batch.ts
Normal file
55
src/main/data/api/handlers/batch.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Batch and Transaction API Handlers
|
||||
*
|
||||
* Implements cross-domain batch processing and atomic transaction operations.
|
||||
*/
|
||||
|
||||
import type { ApiHandler, ApiMethods } from '@shared/data/api/apiTypes'
|
||||
import type { BatchSchemas } from '@shared/data/api/schemas/batch'
|
||||
|
||||
/**
|
||||
* Handler type for a specific batch endpoint
|
||||
*/
|
||||
type BatchHandler<Path extends keyof BatchSchemas, Method extends ApiMethods<Path>> = ApiHandler<Path, Method>
|
||||
|
||||
/**
|
||||
* Batch API handlers implementation
|
||||
*/
|
||||
export const batchHandlers: {
|
||||
[Path in keyof BatchSchemas]: {
|
||||
[Method in keyof BatchSchemas[Path]]: BatchHandler<Path, Method & ApiMethods<Path>>
|
||||
}
|
||||
} = {
|
||||
'/batch': {
|
||||
POST: async ({ body }) => {
|
||||
// Mock batch implementation - can be enhanced with actual batch processing
|
||||
const { requests } = body
|
||||
|
||||
const results = requests.map(() => ({
|
||||
status: 200,
|
||||
data: { processed: true, timestamp: new Date().toISOString() }
|
||||
}))
|
||||
|
||||
return {
|
||||
results,
|
||||
metadata: {
|
||||
duration: Math.floor(Math.random() * 500) + 100,
|
||||
successCount: requests.length,
|
||||
errorCount: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/transaction': {
|
||||
POST: async ({ body }) => {
|
||||
// Mock transaction implementation - can be enhanced with actual transaction support
|
||||
const { operations } = body
|
||||
|
||||
return operations.map(() => ({
|
||||
status: 200,
|
||||
data: { executed: true, timestamp: new Date().toISOString() }
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,210 +1,38 @@
|
||||
/**
|
||||
* Complete API handler implementation
|
||||
* API Handlers Index
|
||||
*
|
||||
* This file implements ALL endpoints defined in ApiSchemas.
|
||||
* TypeScript will error if any endpoint is missing.
|
||||
* Combines all domain-specific handlers into a unified apiHandlers object.
|
||||
* TypeScript will error if any endpoint from ApiSchemas is missing.
|
||||
*
|
||||
* Handler files are organized by domain:
|
||||
* - test.ts - Test API handlers
|
||||
* - batch.ts - Batch and transaction handlers
|
||||
*
|
||||
* @example Adding a new domain:
|
||||
* ```typescript
|
||||
* import { topicHandlers } from './topic'
|
||||
*
|
||||
* export const apiHandlers: ApiImplementation = {
|
||||
* ...testHandlers,
|
||||
* ...batchHandlers,
|
||||
* ...topicHandlers // Add new domain handlers here
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { TestService } from '@data/services/TestService'
|
||||
import type { ApiImplementation } from '@shared/data/api/apiSchemas'
|
||||
import type { ApiImplementation } from '@shared/data/api/apiTypes'
|
||||
|
||||
// Service instances
|
||||
const testService = TestService.getInstance()
|
||||
import { batchHandlers } from './batch'
|
||||
import { testHandlers } from './test'
|
||||
|
||||
/**
|
||||
* Complete API handlers implementation
|
||||
* Must implement every path+method combination from ApiSchemas
|
||||
*
|
||||
* Handlers are spread from individual domain modules for maintainability.
|
||||
* TypeScript ensures exhaustive coverage - missing handlers cause compile errors.
|
||||
*/
|
||||
export const apiHandlers: ApiImplementation = {
|
||||
'/test/items': {
|
||||
GET: async ({ query }) => {
|
||||
return await testService.getItems({
|
||||
page: (query as any)?.page,
|
||||
limit: (query as any)?.limit,
|
||||
search: (query as any)?.search,
|
||||
type: (query as any)?.type,
|
||||
status: (query as any)?.status
|
||||
})
|
||||
},
|
||||
|
||||
POST: async ({ body }) => {
|
||||
return await testService.createItem({
|
||||
title: body.title,
|
||||
description: body.description,
|
||||
type: body.type,
|
||||
status: body.status,
|
||||
priority: body.priority,
|
||||
tags: body.tags,
|
||||
metadata: body.metadata
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
'/test/items/:id': {
|
||||
GET: async ({ params }) => {
|
||||
const item = await testService.getItemById(params.id)
|
||||
if (!item) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return item
|
||||
},
|
||||
|
||||
PUT: async ({ params, body }) => {
|
||||
const item = await testService.updateItem(params.id, {
|
||||
title: body.title,
|
||||
description: body.description,
|
||||
type: body.type,
|
||||
status: body.status,
|
||||
priority: body.priority,
|
||||
tags: body.tags,
|
||||
metadata: body.metadata
|
||||
})
|
||||
if (!item) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return item
|
||||
},
|
||||
|
||||
DELETE: async ({ params }) => {
|
||||
const deleted = await testService.deleteItem(params.id)
|
||||
if (!deleted) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
|
||||
'/test/search': {
|
||||
GET: async ({ query }) => {
|
||||
return await testService.searchItems(query.query, {
|
||||
page: query.page,
|
||||
limit: query.limit,
|
||||
filters: {
|
||||
type: query.type,
|
||||
status: query.status
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
'/test/stats': {
|
||||
GET: async () => {
|
||||
return await testService.getStats()
|
||||
}
|
||||
},
|
||||
|
||||
'/test/bulk': {
|
||||
POST: async ({ body }) => {
|
||||
return await testService.bulkOperation(body.operation, body.data)
|
||||
}
|
||||
},
|
||||
|
||||
'/test/error': {
|
||||
POST: async ({ body }) => {
|
||||
return await testService.simulateError(body.errorType)
|
||||
}
|
||||
},
|
||||
|
||||
'/test/slow': {
|
||||
POST: async ({ body }) => {
|
||||
const delay = body.delay
|
||||
await new Promise((resolve) => setTimeout(resolve, delay))
|
||||
return {
|
||||
message: `Slow response completed after ${delay}ms`,
|
||||
delay,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/reset': {
|
||||
POST: async () => {
|
||||
await testService.resetData()
|
||||
return {
|
||||
message: 'Test data reset successfully',
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/config': {
|
||||
GET: async () => {
|
||||
return {
|
||||
environment: 'test',
|
||||
version: '1.0.0',
|
||||
debug: true,
|
||||
features: {
|
||||
bulkOperations: true,
|
||||
search: true,
|
||||
statistics: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
PUT: async ({ body }) => {
|
||||
return {
|
||||
...body,
|
||||
updated: true,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/status': {
|
||||
GET: async () => {
|
||||
return {
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
version: '1.0.0',
|
||||
uptime: Math.floor(process.uptime()),
|
||||
environment: 'test'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/performance': {
|
||||
GET: async () => {
|
||||
const memUsage = process.memoryUsage()
|
||||
return {
|
||||
requestsPerSecond: Math.floor(Math.random() * 100) + 50,
|
||||
averageLatency: Math.floor(Math.random() * 200) + 50,
|
||||
memoryUsage: memUsage.heapUsed / 1024 / 1024, // MB
|
||||
cpuUsage: Math.random() * 100,
|
||||
uptime: Math.floor(process.uptime())
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/batch': {
|
||||
POST: async ({ body }) => {
|
||||
// Mock batch implementation - can be enhanced with actual batch processing
|
||||
const { requests } = body
|
||||
|
||||
const results = requests.map(() => ({
|
||||
status: 200,
|
||||
data: { processed: true, timestamp: new Date().toISOString() }
|
||||
}))
|
||||
|
||||
return {
|
||||
results,
|
||||
metadata: {
|
||||
duration: Math.floor(Math.random() * 500) + 100,
|
||||
successCount: requests.length,
|
||||
errorCount: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/transaction': {
|
||||
POST: async ({ body }) => {
|
||||
// Mock transaction implementation - can be enhanced with actual transaction support
|
||||
const { operations } = body
|
||||
|
||||
return operations.map(() => ({
|
||||
status: 200,
|
||||
data: { executed: true, timestamp: new Date().toISOString() }
|
||||
}))
|
||||
}
|
||||
}
|
||||
...testHandlers,
|
||||
...batchHandlers
|
||||
}
|
||||
|
||||
185
src/main/data/api/handlers/test.ts
Normal file
185
src/main/data/api/handlers/test.ts
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Test API Handlers
|
||||
*
|
||||
* Implements all test-related API endpoints for development and testing purposes.
|
||||
*/
|
||||
|
||||
import { TestService } from '@data/services/TestService'
|
||||
import type { ApiHandler, ApiMethods } from '@shared/data/api/apiTypes'
|
||||
import type { TestSchemas } from '@shared/data/api/schemas/test'
|
||||
|
||||
// Service instance
|
||||
const testService = TestService.getInstance()
|
||||
|
||||
/**
|
||||
* Handler type for a specific test endpoint
|
||||
*/
|
||||
type TestHandler<Path extends keyof TestSchemas, Method extends ApiMethods<Path>> = ApiHandler<Path, Method>
|
||||
|
||||
/**
|
||||
* Test API handlers implementation
|
||||
*/
|
||||
export const testHandlers: {
|
||||
[Path in keyof TestSchemas]: {
|
||||
[Method in keyof TestSchemas[Path]]: TestHandler<Path, Method & ApiMethods<Path>>
|
||||
}
|
||||
} = {
|
||||
'/test/items': {
|
||||
GET: async ({ query }) => {
|
||||
return await testService.getItems({
|
||||
page: (query as any)?.page,
|
||||
limit: (query as any)?.limit,
|
||||
search: (query as any)?.search,
|
||||
type: (query as any)?.type,
|
||||
status: (query as any)?.status
|
||||
})
|
||||
},
|
||||
|
||||
POST: async ({ body }) => {
|
||||
return await testService.createItem({
|
||||
title: body.title,
|
||||
description: body.description,
|
||||
type: body.type,
|
||||
status: body.status,
|
||||
priority: body.priority,
|
||||
tags: body.tags,
|
||||
metadata: body.metadata
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
'/test/items/:id': {
|
||||
GET: async ({ params }) => {
|
||||
const item = await testService.getItemById(params.id)
|
||||
if (!item) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return item
|
||||
},
|
||||
|
||||
PUT: async ({ params, body }) => {
|
||||
const item = await testService.updateItem(params.id, {
|
||||
title: body.title,
|
||||
description: body.description,
|
||||
type: body.type,
|
||||
status: body.status,
|
||||
priority: body.priority,
|
||||
tags: body.tags,
|
||||
metadata: body.metadata
|
||||
})
|
||||
if (!item) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return item
|
||||
},
|
||||
|
||||
DELETE: async ({ params }) => {
|
||||
const deleted = await testService.deleteItem(params.id)
|
||||
if (!deleted) {
|
||||
throw new Error(`Test item not found: ${params.id}`)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
|
||||
'/test/search': {
|
||||
GET: async ({ query }) => {
|
||||
return await testService.searchItems(query.query, {
|
||||
page: query.page,
|
||||
limit: query.limit,
|
||||
filters: {
|
||||
type: query.type,
|
||||
status: query.status
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
'/test/stats': {
|
||||
GET: async () => {
|
||||
return await testService.getStats()
|
||||
}
|
||||
},
|
||||
|
||||
'/test/bulk': {
|
||||
POST: async ({ body }) => {
|
||||
return await testService.bulkOperation(body.operation, body.data)
|
||||
}
|
||||
},
|
||||
|
||||
'/test/error': {
|
||||
POST: async ({ body }) => {
|
||||
return await testService.simulateError(body.errorType)
|
||||
}
|
||||
},
|
||||
|
||||
'/test/slow': {
|
||||
POST: async ({ body }) => {
|
||||
const delay = body.delay
|
||||
await new Promise((resolve) => setTimeout(resolve, delay))
|
||||
return {
|
||||
message: `Slow response completed after ${delay}ms`,
|
||||
delay,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/reset': {
|
||||
POST: async () => {
|
||||
await testService.resetData()
|
||||
return {
|
||||
message: 'Test data reset successfully',
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/config': {
|
||||
GET: async () => {
|
||||
return {
|
||||
environment: 'test',
|
||||
version: '1.0.0',
|
||||
debug: true,
|
||||
features: {
|
||||
bulkOperations: true,
|
||||
search: true,
|
||||
statistics: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
PUT: async ({ body }) => {
|
||||
return {
|
||||
...body,
|
||||
updated: true,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/status': {
|
||||
GET: async () => {
|
||||
return {
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
version: '1.0.0',
|
||||
uptime: Math.floor(process.uptime()),
|
||||
environment: 'test'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'/test/performance': {
|
||||
GET: async () => {
|
||||
const memUsage = process.memoryUsage()
|
||||
return {
|
||||
requestsPerSecond: Math.floor(Math.random() * 100) + 50,
|
||||
averageLatency: Math.floor(Math.random() * 200) + 50,
|
||||
memoryUsage: memUsage.heapUsed / 1024 / 1024, // MB
|
||||
cpuUsage: Math.random() * 100,
|
||||
uptime: Math.floor(process.uptime())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,6 @@ export { apiHandlers } from './handlers'
|
||||
export { TestService } from '@data/services/TestService'
|
||||
|
||||
// Re-export types for convenience
|
||||
export type { CreateTestItemDto, TestItem, UpdateTestItemDto } from '@shared/data/api'
|
||||
export type {
|
||||
DataRequest,
|
||||
DataResponse,
|
||||
@ -30,3 +29,4 @@ export type {
|
||||
RequestContext,
|
||||
ServiceOptions
|
||||
} from '@shared/data/api/apiTypes'
|
||||
export type { CreateTestItemDto, TestItem, UpdateTestItemDto } from '@shared/data/api/schemas/test'
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
*/
|
||||
|
||||
import { loggerService } from '@logger'
|
||||
import type { ApiClient, ConcreteApiPaths } from '@shared/data/api/apiSchemas'
|
||||
import type { ApiClient, ConcreteApiPaths } from '@shared/data/api/apiTypes'
|
||||
import type {
|
||||
BatchRequest,
|
||||
BatchResponse,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { BodyForPath, QueryParamsForPath, ResponseForPath } from '@shared/data/api/apiPaths'
|
||||
import type { ConcreteApiPaths } from '@shared/data/api/apiSchemas'
|
||||
import type { ConcreteApiPaths } from '@shared/data/api/apiTypes'
|
||||
import type { PaginatedResponse } from '@shared/data/api/apiTypes'
|
||||
import { useState } from 'react'
|
||||
import type { KeyedMutator } from 'swr'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ApiClient, ConcreteApiPaths } from '@shared/data/api/apiSchemas'
|
||||
import type { ApiClient, ConcreteApiPaths } from '@shared/data/api/apiTypes'
|
||||
import type { DataResponse } from '@shared/data/api/apiTypes'
|
||||
import { vi } from 'vitest'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ConcreteApiPaths } from '@shared/data/api/apiSchemas'
|
||||
import type { ConcreteApiPaths } from '@shared/data/api/apiTypes'
|
||||
import type { PaginatedResponse } from '@shared/data/api/apiTypes'
|
||||
import { vi } from 'vitest'
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user