bunsane 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +152 -0
- package/gql/visitors/SchemaGeneratorVisitor.ts +15 -12
- package/package.json +1 -1
- package/.claude/skills/update-memory.md +0 -74
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
BunSane is an experimental TypeScript API framework for Bun using Entity-Component-System (ECS) architecture with PostgreSQL storage. It provides GraphQL Yoga integration with automatic schema generation from decorated services.
|
|
8
|
+
|
|
9
|
+
**Stack**: Bun, TypeScript, PostgreSQL, GraphQL Yoga, GQLoom, Zod 4.x, Pino logging
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install dependencies
|
|
15
|
+
bun install
|
|
16
|
+
|
|
17
|
+
# Build (includes studio + typecheck)
|
|
18
|
+
bun run build
|
|
19
|
+
|
|
20
|
+
# Typecheck only
|
|
21
|
+
tsc --noEmit
|
|
22
|
+
|
|
23
|
+
# Tests (requires PostgreSQL)
|
|
24
|
+
bun test # Unit + integration + GraphQL tests
|
|
25
|
+
bun run test:unit # Unit tests only
|
|
26
|
+
bun run test:integration # Integration tests only
|
|
27
|
+
bun run test:graphql # GraphQL schema tests only
|
|
28
|
+
|
|
29
|
+
# Tests with PGlite (no PostgreSQL required)
|
|
30
|
+
bun run test:pglite # All tests with PGlite
|
|
31
|
+
bun run test:pglite:unit # Unit tests only with PGlite
|
|
32
|
+
|
|
33
|
+
# E2E and stress tests
|
|
34
|
+
bun run test:e2e # HTTP tests (no DB needed)
|
|
35
|
+
bun run test:stress # Performance benchmarks
|
|
36
|
+
|
|
37
|
+
# Run single test file
|
|
38
|
+
bun test path/to/file.test.ts
|
|
39
|
+
|
|
40
|
+
# Run tests matching pattern
|
|
41
|
+
bun test --grep "pattern"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Architecture
|
|
45
|
+
|
|
46
|
+
### ECS Model
|
|
47
|
+
- **Entities**: Generic containers with UUID, stored in `entities` table
|
|
48
|
+
- **Components**: Data containers attached to entities, stored in `components` table with JSONB data
|
|
49
|
+
- **Services**: Business logic with GraphQL operations
|
|
50
|
+
|
|
51
|
+
### Core Classes
|
|
52
|
+
|
|
53
|
+
**Entity** (`core/Entity.ts`):
|
|
54
|
+
```typescript
|
|
55
|
+
const entity = Entity.Create();
|
|
56
|
+
entity.add(Position, { x: 0, y: 0 }); // Add component
|
|
57
|
+
await entity.set(Position, { x: 10 }); // Update component
|
|
58
|
+
const pos = await entity.get(Position); // Get component data
|
|
59
|
+
await entity.save();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**BaseComponent** (`core/components/BaseComponent.ts`):
|
|
63
|
+
```typescript
|
|
64
|
+
@Component
|
|
65
|
+
class Position extends BaseComponent {
|
|
66
|
+
@CompData() x: number = 0;
|
|
67
|
+
@CompData({ indexed: true }) y: number = 0; // Creates DB index
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**BaseArcheType** (`core/ArcheType.ts`):
|
|
72
|
+
- Predefined entity templates with required components
|
|
73
|
+
- Auto-generates GraphQL types and CRUD operations
|
|
74
|
+
|
|
75
|
+
**Query** (`query/Query.ts`):
|
|
76
|
+
```typescript
|
|
77
|
+
const entities = await new Query()
|
|
78
|
+
.with(Position) // Require component
|
|
79
|
+
.with(Velocity, { filters: [...] }) // With filters
|
|
80
|
+
.populate() // Load all components
|
|
81
|
+
.limit(10)
|
|
82
|
+
.exec();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**BaseService** (`service/Service.ts`):
|
|
86
|
+
```typescript
|
|
87
|
+
class UserService extends BaseService {
|
|
88
|
+
@GraphQLOperation({ type: "Query", input: { id: t.id() }, output: User })
|
|
89
|
+
async getUser(input: { id: string }, ctx: GraphQLContext) { ... }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### GraphQL
|
|
94
|
+
|
|
95
|
+
- Schema generated automatically from decorated services and archetypes
|
|
96
|
+
- Input types use Schema DSL (`gql/schema/index.ts`) via `t.` API
|
|
97
|
+
- Operations: `@GraphQLOperation`, `@GraphQLSubscription`
|
|
98
|
+
- Archetypes: `@ArcheTypeFunction` for computed fields
|
|
99
|
+
|
|
100
|
+
### Database
|
|
101
|
+
|
|
102
|
+
- PostgreSQL with Bun's native SQL driver (`Bun.SQL`)
|
|
103
|
+
- Auto-migrations on startup for base tables
|
|
104
|
+
- Component data stored as JSONB
|
|
105
|
+
- Indexed fields create GIN indexes automatically
|
|
106
|
+
|
|
107
|
+
### Caching
|
|
108
|
+
|
|
109
|
+
- Multi-level cache: L1 (memory) + L2 (Redis)
|
|
110
|
+
- `CacheManager.initialize(config)` is async - always await it
|
|
111
|
+
- Strategies: write-through, write-invalidate
|
|
112
|
+
- Cross-instance invalidation via Redis pub/sub
|
|
113
|
+
|
|
114
|
+
### File Uploads
|
|
115
|
+
|
|
116
|
+
- `UploadManager` handles file validation and storage
|
|
117
|
+
- `S3StorageProvider` for S3-compatible storage (AWS, MinIO, R2)
|
|
118
|
+
- REST: `handleUpload(req)` from `upload/RestUpload.ts`
|
|
119
|
+
- GraphQL: `@Upload()` decorator
|
|
120
|
+
|
|
121
|
+
## Critical Rules
|
|
122
|
+
|
|
123
|
+
### Import Style
|
|
124
|
+
**ALWAYS use relative imports** (`./`, `../`) for internal modules. Never use bare imports like `from "core/Logger"` - this breaks consumer typechecking.
|
|
125
|
+
|
|
126
|
+
### Architecture Decisions
|
|
127
|
+
- No Dependency Injection - uses singletons + global exports
|
|
128
|
+
- Singleton access: `CacheManager.getInstance()`, `EntityManager.instance`, etc.
|
|
129
|
+
- Services registered via `ServiceRegistry.register()`
|
|
130
|
+
|
|
131
|
+
### Test Database Setup
|
|
132
|
+
- Tests require `.env.test` with PostgreSQL config (or use PGlite mode)
|
|
133
|
+
- `bunfig.toml` preloads `tests/setup.ts`
|
|
134
|
+
- PGlite: `CREATE INDEX CONCURRENTLY` must check `process.env.USE_PGLITE`
|
|
135
|
+
- PGlite JSONB: pass JS objects directly, never `JSON.stringify() + ::jsonb`
|
|
136
|
+
|
|
137
|
+
## Directory Structure
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
core/ # ECS core: Entity, Component, ArcheType, App
|
|
141
|
+
cache/ # CacheManager, Redis, Memory caches
|
|
142
|
+
components/ # BaseComponent, decorators, registry
|
|
143
|
+
middleware/ # HTTP middleware (AccessLog, RequestId, SecurityHeaders)
|
|
144
|
+
database/ # DatabaseHelper, SQL utilities
|
|
145
|
+
gql/ # GraphQL generation, Schema DSL
|
|
146
|
+
schema/ # t.* Schema DSL for inputs
|
|
147
|
+
query/ # Fluent Query builder, FilterBuilder
|
|
148
|
+
service/ # BaseService, ServiceRegistry
|
|
149
|
+
upload/ # File uploads, S3StorageProvider
|
|
150
|
+
scheduler/ # Cron-style task scheduling
|
|
151
|
+
tests/ # Unit, integration, GraphQL, E2E, stress tests
|
|
152
|
+
```
|
|
@@ -255,21 +255,20 @@ export class SchemaGeneratorVisitor extends GraphVisitor {
|
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
// Handle output
|
|
259
|
-
const outputType = this.extractOutputType(output);
|
|
258
|
+
// Handle output
|
|
259
|
+
const outputType = this.extractOutputType(output, name);
|
|
260
260
|
fieldDef += `: ${outputType}`;
|
|
261
|
-
|
|
261
|
+
|
|
262
262
|
return fieldDef;
|
|
263
263
|
}
|
|
264
|
-
|
|
264
|
+
|
|
265
265
|
/**
|
|
266
|
-
* Extract output type
|
|
266
|
+
* Extract output type from operation metadata
|
|
267
267
|
*/
|
|
268
|
-
private extractOutputType(output: any): string {
|
|
268
|
+
private extractOutputType(output: any, operationName: string): string {
|
|
269
269
|
if (typeof output === 'string') {
|
|
270
270
|
return output;
|
|
271
271
|
} else if (Array.isArray(output)) {
|
|
272
|
-
// Handle array of archetypes: [serviceAreaArcheType]
|
|
273
272
|
const archetypeInstance = output[0];
|
|
274
273
|
const typeName = this.getArchetypeTypeName(archetypeInstance);
|
|
275
274
|
if (typeName) {
|
|
@@ -279,7 +278,6 @@ export class SchemaGeneratorVisitor extends GraphVisitor {
|
|
|
279
278
|
return '[Any]';
|
|
280
279
|
}
|
|
281
280
|
} else if (output instanceof BaseArcheType) {
|
|
282
|
-
// Handle single archetype instance: serviceAreaArcheType
|
|
283
281
|
const typeName = this.getArchetypeTypeName(output);
|
|
284
282
|
if (typeName) {
|
|
285
283
|
return typeName;
|
|
@@ -288,11 +286,16 @@ export class SchemaGeneratorVisitor extends GraphVisitor {
|
|
|
288
286
|
return 'Any';
|
|
289
287
|
}
|
|
290
288
|
} else if (typeof output === 'object' && output !== null) {
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
289
|
+
// Inline object output — generate a named GraphQL output type
|
|
290
|
+
const capitalizedName = operationName.charAt(0).toUpperCase() + operationName.slice(1);
|
|
291
|
+
const outputTypeName = `${capitalizedName}Output`;
|
|
292
|
+
if (!this.definedTypes.has(outputTypeName)) {
|
|
293
|
+
const outputTypeDef = `type ${outputTypeName} {\n${Object.entries(output).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
|
|
294
|
+
this.typeDefs += outputTypeDef;
|
|
295
|
+
this.definedTypes.add(outputTypeName);
|
|
296
|
+
}
|
|
297
|
+
return outputTypeName;
|
|
294
298
|
} else {
|
|
295
|
-
// Default case when output is not specified - assume String (like V1)
|
|
296
299
|
return 'String';
|
|
297
300
|
}
|
|
298
301
|
}
|
package/package.json
CHANGED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: update-memory
|
|
3
|
-
description: Updating project memories with new information
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
allowed-tools: Read, Grep
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Update Memory Skill
|
|
9
|
-
|
|
10
|
-
This skill helps you update Serena project memories with new or changed information.
|
|
11
|
-
|
|
12
|
-
## When to Use
|
|
13
|
-
|
|
14
|
-
Use `/update-memory` when you need to:
|
|
15
|
-
- Record architectural decisions or patterns discovered during work
|
|
16
|
-
- Update project conventions based on new practices
|
|
17
|
-
- Document important findings from code exploration
|
|
18
|
-
- Add new information to existing memory files
|
|
19
|
-
- Create new memory files for undocumented aspects
|
|
20
|
-
|
|
21
|
-
## Instructions
|
|
22
|
-
|
|
23
|
-
1. **List existing memories** first to see what's available:
|
|
24
|
-
- Use `mcp__serena__list_memories` to see all memory files
|
|
25
|
-
|
|
26
|
-
2. **Read the relevant memory** (if updating existing):
|
|
27
|
-
- Use `mcp__serena__read_memory` with the memory file name
|
|
28
|
-
- Understand the current content and structure
|
|
29
|
-
|
|
30
|
-
3. **Determine the action**:
|
|
31
|
-
- **Update existing**: Use `mcp__serena__edit_memory` for targeted changes
|
|
32
|
-
- **Create new**: Use `mcp__serena__write_memory` for new topics
|
|
33
|
-
- **Delete obsolete**: Use `mcp__serena__delete_memory` (only if user confirms)
|
|
34
|
-
|
|
35
|
-
4. **For updates**, use `edit_memory` with:
|
|
36
|
-
- `mode: "literal"` for exact string replacement
|
|
37
|
-
- `mode: "regex"` for pattern-based changes
|
|
38
|
-
- Keep the existing format and style
|
|
39
|
-
|
|
40
|
-
5. **For new memories**, use descriptive kebab-case names:
|
|
41
|
-
- `project_overview` - General project info
|
|
42
|
-
- `architecture` - System architecture
|
|
43
|
-
- `code_style_and_conventions` - Coding standards
|
|
44
|
-
- `{feature}-implementation` - Feature-specific docs
|
|
45
|
-
|
|
46
|
-
## Memory Naming Conventions
|
|
47
|
-
|
|
48
|
-
| Pattern | Purpose |
|
|
49
|
-
|---------|---------|
|
|
50
|
-
| `project_overview` | High-level project description |
|
|
51
|
-
| `architecture` | Directory structure, core concepts |
|
|
52
|
-
| `code_style_and_conventions` | Formatting, naming, patterns |
|
|
53
|
-
| `{topic}-decisions` | Architectural decisions on topic |
|
|
54
|
-
| `{feature}-implementation` | How a feature works |
|
|
55
|
-
| `suggested_commands` | Useful commands for the project |
|
|
56
|
-
| `task_completion_checklist` | Steps to verify work is complete |
|
|
57
|
-
|
|
58
|
-
## Example Usage
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
User: /update-memory Add that we use Bun for testing
|
|
62
|
-
Assistant: [Lists memories, reads code_style_and_conventions, updates with new testing info]
|
|
63
|
-
|
|
64
|
-
User: /update-memory Create a memory about the caching system
|
|
65
|
-
Assistant: [Creates new memory file: caching-system.md with relevant information]
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Important Notes
|
|
69
|
-
|
|
70
|
-
- Always read existing content before editing to preserve context
|
|
71
|
-
- Use markdown format for memory content
|
|
72
|
-
- Keep memories focused and organized
|
|
73
|
-
- Ask for confirmation before deleting memories
|
|
74
|
-
- After updating, summarize what was changed
|