From 16045ced496361c6113c447a79c8a987a92dc557 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 17 Jul 2025 12:56:42 -0600 Subject: [PATCH] Added readme and other stuff for cursor --- packages/server-shared/.cursor/global.mdc | 163 ++++++++++++--- packages/server-shared/README.md | 234 ++++++++++++++++++++++ packages/server-shared/src/index.ts | 2 - 3 files changed, 372 insertions(+), 27 deletions(-) create mode 100644 packages/server-shared/README.md delete mode 100644 packages/server-shared/src/index.ts diff --git a/packages/server-shared/.cursor/global.mdc b/packages/server-shared/.cursor/global.mdc index e1102e161..494bb2e1b 100644 --- a/packages/server-shared/.cursor/global.mdc +++ b/packages/server-shared/.cursor/global.mdc @@ -1,48 +1,161 @@ --- globs: src/* alwaysApply: false -description: Global rules for server-shared folder +description: Global rules for server-shared folder - Shared TypeScript types and schemas --- - ## Overview of the `src` Folder Structure -The `src` folder is organized to reflect the structure of our API endpoints and general concepts such as organization, user, team, etc. Each of these concepts is encapsulated within its own folder. +The `src` folder is organized to reflect the structure of our API endpoints and general concepts such as organization, user, team, etc. Each of these concepts is encapsulated within its own folder following strict patterns to ensure consistency across frontend and backend services. -### Folder Structure +### Core Architecture Principles -- **Endpoint and Concept Folders**: Each folder corresponds to a specific endpoint or concept. Within these folders, you will find: - - `request.ts`: Defines the request types using Zod schemas. - - `response.ts`: Defines the response types using Zod schemas. - - `index.ts`: Exports the types defined in `request.ts` and `response.ts`. +#### 1. Zod-First Approach (MANDATORY) +All types MUST be defined using Zod schemas first, then TypeScript types are inferred from them: -- **Additional Files**: Some folders may contain additional files to assist in defining types specific to that folder's context. +```typescript +// ✅ CORRECT: Define schema first +export const UserSchema = z.object({ + id: z.string(), + email: z.string().email(), + name: z.string(), +}); -### Type Definition +export type User = z.infer; -- **Zod Schemas**: All types are defined using Zod schemas to ensure type safety and validation. +// ❌ WRONG: Defining types without schemas +export interface User { + id: string; + email: string; + name: string; +} +``` -### Special Folder: `type-utilities` +#### 2. Database Type Imports (CRITICAL) +When referencing database types from `@buster/database`, **ALWAYS** import them as types to avoid compilation errors: -- This folder is dedicated to generic types, such as pagination or query array processing. It provides utility functions and types that can be reused across different folders. +```typescript +// ✅ CORRECT: Import as type +import type { organizations, userOrganizationRoleEnum } from '@buster/database'; + +// ❌ WRONG: Import as value (will cause build failures) +import { organizations } from '@buster/database'; +``` + +### Folder Structure Requirements + +Each module folder MUST contain: +- **`index.ts`**: Barrel exports for all public types in the module +- **`requests.ts`**: Request schemas and types for API endpoints +- **`responses.ts`**: Response schemas and types for API endpoints +- **Additional type files**: As needed for the module's domain + +Example structure: +``` +organization/ +├── index.ts # export * from './organization.types'; etc. +├── organization.types.ts +├── requests.ts # UpdateOrganizationRequestSchema, etc. +├── responses.ts # GetOrganizationResponseSchema, etc. +├── roles.types.ts # Additional domain types +└── user.types.ts +``` + +### Enum Pattern for Database Parity + +When creating enums that mirror database enums, use frozen objects to maintain type safety: + +```typescript +import type { userOrganizationRoleEnum } from '@buster/database'; + +type OrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number]; + +// Create a frozen object that mirrors the database enum +export const OrganizationRoleEnum: Record = + Object.freeze({ + viewer: 'viewer', + workspace_admin: 'workspace_admin', + data_admin: 'data_admin', + querier: 'querier', + restricted_querier: 'restricted_querier', + }); + +// Create Zod schema from the enum +export const OrganizationRoleSchema = z.enum( + Object.values(OrganizationRoleEnum) as [OrganizationRoleBase, ...OrganizationRoleBase[]] +); + +export type OrganizationRole = z.infer; +``` + +### Database Type Parity + +When types are direct copies of database models, use the `Expect` and `Equal` utilities: + +```typescript +import type { organizations } from '@buster/database'; +import type { Equal, Expect } from '../type-utilities'; + +export type Organization = z.infer; + +// This will cause a TypeScript error if the types don't match +type _OrganizationEqualityCheck = Expect>; +``` + +### Special Folders + +#### `type-utilities` +Contains generic utility types that can be reused across modules: +- Pagination types and utilities +- Type equality checking utilities (`Equal`, `Expect`) +- Query array preprocessing utilities +- Other cross-cutting type utilities ### Exporting Types -- **Package.json**: Any new folder should ensure its types are exported via the `package.json` to facilitate proper building and exporting of types. +1. **Module Exports**: Each module must have proper barrel exports in its `index.ts` +2. **Package.json**: New modules MUST be added to the package.json exports: + ```json + "./your-module": { + "types": "./dist/your-module/index.d.ts", + "default": "./dist/your-module/index.js" + } + ``` -This structure ensures a consistent and organized approach to managing types across the application, leveraging Zod for robust type definitions and validation. +### Naming Conventions +- Use either `requests.ts`/`responses.ts` OR `requests.types.ts`/`responses.types.ts` consistently within a module +- Type files should use `.types.ts` suffix (e.g., `organization.types.ts`) +- Test files should use `.test.ts` suffix and be colocated with source files -### Pagination +### Common Patterns -If we need to use pagination for a type we can take advantage of the generic types found in the type-utilities/pagination file - - -## Database partity -There are cirumstances where we need to ensure that a schema in server-shared and a database type are the same. We should use a pattern like this: - -``` - type _OrganizationEqualityCheck = Expect>; +#### Hex Color Validation +```typescript +const HexColorSchema = z + .string() + .regex( + /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, + 'Must be a valid 3 or 6 digit hex color code' + ); ``` -Where organizations is imported as a "type" from @buster/database. This ensure that we are maintaining type parity between the two packages. \ No newline at end of file +#### Pagination +```typescript +import { PaginationParams } from '../type-utilities'; + +export const ListUsersRequestSchema = z.object({ + ...PaginationParams.shape, + filter: z.string().optional(), +}); +``` + +### Critical Rules Summary + +1. **NEVER** import database constants directly - only import as types +2. **ALWAYS** define Zod schemas before TypeScript types +3. **ALWAYS** use `Expect>` for database model type parity +4. **ALWAYS** export modules through package.json +5. **ALWAYS** include requests.ts and responses.ts in each module +6. **NEVER** use interfaces when you can use Zod inference +7. **ALWAYS** use barrel exports through index.ts files \ No newline at end of file diff --git a/packages/server-shared/README.md b/packages/server-shared/README.md new file mode 100644 index 000000000..ed820316f --- /dev/null +++ b/packages/server-shared/README.md @@ -0,0 +1,234 @@ +# @buster/server-shared + +> Shared TypeScript types and schemas for the Buster ecosystem + +## Overview + +The `@buster/server-shared` package provides a centralized location for all shared TypeScript types, Zod schemas, and type utilities used across the Buster monorepo. This package ensures type safety and consistency between frontend and backend services. + +## Architecture Principles + +### 1. Zod-First Approach +All types are defined using Zod schemas first, then TypeScript types are inferred from them: + +```typescript +// ✅ Good: Define schema first +export const UserSchema = z.object({ + id: z.string(), + email: z.string().email(), + name: z.string(), +}); + +export type User = z.infer; + +// ❌ Bad: Defining types without schemas +export interface User { + id: string; + email: string; + name: string; +} +``` + +### 2. Database Type Imports +When referencing database types from `@buster/database`, **ALWAYS** import them as types to avoid compilation errors: + +```typescript +// ✅ Good: Import as type +import type { organizations, userOrganizationRoleEnum } from '@buster/database'; + +// ❌ Bad: Import as value +import { organizations } from '@buster/database'; +``` + +### 3. Enum Pattern for Database Parity +When creating enums that mirror database enums, use frozen objects to maintain type safety: + +```typescript +import type { userOrganizationRoleEnum } from '@buster/database'; + +type OrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number]; + +// Create a frozen object that mirrors the database enum +export const OrganizationRoleEnum: Record = + Object.freeze({ + viewer: 'viewer', + workspace_admin: 'workspace_admin', + data_admin: 'data_admin', + querier: 'querier', + restricted_querier: 'restricted_querier', + }); + +// Create Zod schema from the enum +export const OrganizationRoleSchema = z.enum( + Object.values(OrganizationRoleEnum) as [OrganizationRoleBase, ...OrganizationRoleBase[]] +); + +export type OrganizationRole = z.infer; +``` + +### 4. Type Parity Checks +When types are direct copies of database models, use the `Expect` and `Equal` utilities to ensure parity: + +```typescript +import type { organizations } from '@buster/database'; +import type { Equal, Expect } from '../type-utilities'; + +export type Organization = z.infer; + +// This will cause a TypeScript error if the types don't match +type _OrganizationEqualityCheck = Expect>; +``` + +## Directory Structure + +``` +src/ +├── index.ts # Main barrel export (currently incomplete) +├── chats/ # Chat-related types +│ ├── index.ts # Barrel exports for chats +│ ├── chat.types.ts # Core chat types +│ ├── chat-message.types.ts +│ └── ... +├── organization/ # Organization types +│ ├── index.ts # Barrel exports +│ ├── organization.types.ts +│ ├── requests.ts # Request schemas/types +│ ├── responses.ts # Response schemas/types +│ └── ... +├── metrics/ # Metrics types +│ ├── index.ts +│ ├── requests.types.ts # Note: Some use .types.ts suffix +│ ├── responses.types.ts +│ └── ... +├── type-utilities/ # Generic utility types +│ ├── index.ts +│ ├── pagination.ts # Pagination utilities +│ ├── isEqual.ts # Type equality checks +│ └── ... +└── ... (other modules) +``` + +## Module Structure Guidelines + +Each module should follow this structure: + +### 1. Barrel Export (index.ts) +Export all public types from the module: + +```typescript +export * from './organization.types'; +export * from './roles.types'; +export * from './requests'; +export * from './responses'; +``` + +### 2. Request Types (requests.ts) +Define all request schemas for the module: + +```typescript +import { z } from 'zod'; + +export const CreateUserRequestSchema = z.object({ + email: z.string().email(), + name: z.string(), +}); + +export type CreateUserRequest = z.infer; +``` + +### 3. Response Types (responses.ts) +Define all response schemas for the module: + +```typescript +import { z } from 'zod'; +import { UserSchema } from './user.types'; + +export const GetUserResponseSchema = UserSchema; +export type GetUserResponse = z.infer; + +export const ListUsersResponseSchema = z.object({ + users: z.array(UserSchema), + total: z.number(), +}); +export type ListUsersResponse = z.infer; +``` + +## Usage Examples + +### Importing Types +```typescript +// Import from specific modules +import { Organization, UpdateOrganizationRequest } from '@buster/server-shared/organization'; +import { Chat, ChatMessage } from '@buster/server-shared/chats'; +import { PaginationParams } from '@buster/server-shared/type-utilities'; +``` + +### Using Schemas for Validation +```typescript +import { UpdateOrganizationRequestSchema } from '@buster/server-shared/organization'; + +// Validate incoming data +const validatedData = UpdateOrganizationRequestSchema.parse(requestBody); + +// Or safe parse with error handling +const result = UpdateOrganizationRequestSchema.safeParse(requestBody); +if (!result.success) { + console.error(result.error); +} +``` + +## Adding New Modules + +1. Create a new directory under `src/` +2. Add the following files: + - `index.ts` - Barrel exports + - `requests.ts` - Request schemas/types + - `responses.ts` - Response schemas/types + - Additional type files as needed +3. Update `package.json` exports: + ```json + "./your-module": { + "types": "./dist/your-module/index.d.ts", + "default": "./dist/your-module/index.js" + } + ``` + +## Common Patterns + +### Hex Color Validation +```typescript +const HexColorSchema = z + .string() + .regex( + /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, + 'Must be a valid 3 or 6 digit hex color code' + ); +``` + +### Pagination +Use the generic pagination types from `type-utilities`: +```typescript +import { PaginationParams } from '../type-utilities'; + +export const ListUsersRequestSchema = z.object({ + ...PaginationParams.shape, + filter: z.string().optional(), +}); +``` + +## Important Notes + +1. **Never import database constants directly** - This will cause build failures in frontend packages +2. **Always define schemas before types** - Types should be inferred from schemas +3. **Maintain type parity** - Use `Expect>` pattern for database model copies +4. **Export through package.json** - Each module needs its own export entry +5. **Follow naming conventions** - Use either `requests.ts/responses.ts` or `requests.types.ts/responses.types.ts` consistently within a module + +## Contributing + +When adding new types or modifying existing ones: +1. Follow the Zod-first approach +2. Ensure proper barrel exports +3. Add request/response types where applicable +4. Update package.json exports if adding new modules +5. Run type checking before committing \ No newline at end of file diff --git a/packages/server-shared/src/index.ts b/packages/server-shared/src/index.ts deleted file mode 100644 index 6a0d7b478..000000000 --- a/packages/server-shared/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export all modules -export * from './chats/index';