mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-11 20:00:23 +01:00
.
This commit is contained in:
234
claude.md
Normal file
234
claude.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Monorepo Architecture
|
||||
|
||||
This is a pnpm workspace monorepo with two packages:
|
||||
|
||||
- **`packages/worker`**: Cloudflare Worker (backend)
|
||||
- **`packages/web`**: TanStack Start app (frontend)
|
||||
|
||||
### Key Architectural Pattern: Worker RPC via Service Bindings
|
||||
|
||||
The web package communicates with the worker package through **two mechanisms**:
|
||||
|
||||
1. **HTTP API** (traditional): REST endpoints exposed by Hono in `packages/worker/src/index.ts`
|
||||
2. **RPC calls** (service bindings): Type-safe method calls via `WorkerRpc` class in `packages/worker/src/rpc.ts`
|
||||
|
||||
**Critical understanding**: The worker exports TWO things from `src/index.ts`:
|
||||
|
||||
- `export default app` - Hono HTTP handler (default export)
|
||||
- `export { WorkerRpc } from './rpc'` - Named export for RPC entrypoint
|
||||
|
||||
The web package's `wrangler.jsonc` configures a service binding:
|
||||
|
||||
```jsonc
|
||||
"services": [{
|
||||
"binding": "WORKER_RPC",
|
||||
"service": "fullstack-monorepo-template-worker",
|
||||
"entrypoint": "WorkerRpc" // References the named export
|
||||
}]
|
||||
```
|
||||
|
||||
### Type Safety Across Packages
|
||||
|
||||
The web package imports types directly from the worker package:
|
||||
|
||||
```typescript
|
||||
// packages/web/env.d.ts
|
||||
import type { WorkerRpc } from '../worker/src/rpc';
|
||||
```
|
||||
|
||||
This creates a **direct TypeScript dependency** between packages. The monorepo structure enables this cross-package type sharing.
|
||||
|
||||
### TanStack Start Server Context
|
||||
|
||||
In TanStack Start server functions/loaders, access the RPC binding via:
|
||||
|
||||
```typescript
|
||||
import { getServerContext } from '@tanstack/react-start/server';
|
||||
|
||||
const { WORKER_RPC } = getServerContext().cloudflare.env;
|
||||
const result = await WORKER_RPC.sayHello('World');
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Run both services in separate terminals
|
||||
pnpm dev:worker # Worker on localhost:8787
|
||||
pnpm dev:web # Web on localhost:3000
|
||||
|
||||
# Or run just one
|
||||
pnpm --filter @linsa/worker dev
|
||||
pnpm --filter @linsa/web dev
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pnpm test
|
||||
|
||||
# Run tests for specific package
|
||||
pnpm --filter @linsa/worker test
|
||||
pnpm --filter @linsa/web test
|
||||
|
||||
# Run tests in watch mode (within package directory)
|
||||
cd packages/worker && pnpm test --watch
|
||||
```
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
```bash
|
||||
pnpm lint # Lint all packages + check formatting
|
||||
pnpm lint:fix # Fix linting issues in all packages
|
||||
pnpm format # Format all code
|
||||
pnpm format:check # Check formatting without changes
|
||||
```
|
||||
|
||||
### Deployment
|
||||
|
||||
```bash
|
||||
# Deploy both packages
|
||||
pnpm deploy
|
||||
|
||||
# Deploy individually
|
||||
pnpm deploy:worker
|
||||
pnpm deploy:web
|
||||
|
||||
# Login to Cloudflare first (one-time)
|
||||
cd packages/worker && pnpm wrangler login
|
||||
```
|
||||
|
||||
### Working with Workspace Packages
|
||||
|
||||
```bash
|
||||
# Add dependency to specific package
|
||||
pnpm --filter @linsa/worker add <package-name>
|
||||
pnpm --filter @linsa/web add <package-name>
|
||||
|
||||
# Add dev dependency
|
||||
pnpm --filter @linsa/worker add -D <package-name>
|
||||
```
|
||||
|
||||
## Adding New RPC Methods
|
||||
|
||||
When adding RPC methods that the web package will call:
|
||||
|
||||
1. **Add method to `packages/worker/src/rpc.ts`**:
|
||||
|
||||
```typescript
|
||||
export class WorkerRpc extends WorkerEntrypoint {
|
||||
async myNewMethod(param: string): Promise<Result> {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **TypeScript will automatically provide types** in the web package because `env.d.ts` imports the `WorkerRpc` type
|
||||
|
||||
3. **Call from web package** in any server function:
|
||||
|
||||
```typescript
|
||||
const { WORKER_RPC } = getServerContext().cloudflare.env;
|
||||
const result = await WORKER_RPC.myNewMethod('value');
|
||||
```
|
||||
|
||||
4. **Optional**: Add helper function in `packages/web/src/lib/worker-rpc.ts` for convenience
|
||||
|
||||
## Adding Cloudflare Bindings
|
||||
|
||||
To add KV, D1, R2, or other bindings to the worker:
|
||||
|
||||
1. Update `packages/worker/wrangler.jsonc`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "MY_KV",
|
||||
"id": "your-namespace-id",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
2. Update TypeScript types in `packages/worker/src/index.ts` or create a separate `env.ts`:
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
MY_KV: KVNamespace;
|
||||
}
|
||||
```
|
||||
|
||||
3. Access in RPC methods or HTTP handlers:
|
||||
|
||||
```typescript
|
||||
// In rpc.ts
|
||||
export class WorkerRpc extends WorkerEntrypoint<Env> {
|
||||
async getData(key: string) {
|
||||
return await this.env.MY_KV.get(key);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Vitest Configuration for Cloudflare Workers
|
||||
|
||||
The worker package uses `@cloudflare/vitest-pool-workers` for testing. The config pattern is:
|
||||
|
||||
```typescript
|
||||
// vitest.config.mts
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
pool: '@cloudflare/vitest-pool-workers',
|
||||
poolOptions: {
|
||||
workers: {
|
||||
wrangler: { configPath: './wrangler.jsonc' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Do not use** `defineWorkersConfig` from `@cloudflare/vitest-pool-workers/config` - it causes TypeScript module resolution issues.
|
||||
|
||||
## pnpm Workspace Configuration
|
||||
|
||||
The `pnpm-workspace.yaml` includes `onlyBuiltDependencies` for native dependencies:
|
||||
|
||||
```yaml
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- sharp
|
||||
- workerd
|
||||
```
|
||||
|
||||
This ensures these packages are built correctly in the monorepo context.
|
||||
|
||||
## Package Naming Convention
|
||||
|
||||
Packages use the `@linsa/*` scope:
|
||||
|
||||
- `@linsa/worker`
|
||||
- `@linsa/web`
|
||||
|
||||
When filtering commands, use these exact names: `pnpm --filter @linsa/worker <command>`
|
||||
|
||||
## ESLint Configuration
|
||||
|
||||
The project uses **ESLint v9** with the new flat config format (`eslint.config.js`).
|
||||
|
||||
Key points:
|
||||
|
||||
- Uses `@typescript-eslint/eslint-plugin` v8+ (compatible with ESLint 9)
|
||||
- Configured for both TypeScript and TSX/JSX files
|
||||
- The `no-undef` rule is disabled for TypeScript files (TypeScript handles this)
|
||||
- Ignores: `node_modules/`, `dist/`, `.wrangler/`, `build/`
|
||||
|
||||
Both packages (`worker` and `web`) have `lint` and `lint:fix` scripts that use the shared root config.
|
||||
Reference in New Issue
Block a user