Skip to content

Commit 0c7e6e1

Browse files
Copilothotlong
andcommitted
Enhance READMEs with AI quick reference and practical examples
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 0802a3e commit 0c7e6e1

4 files changed

Lines changed: 268 additions & 6 deletions

File tree

packages/client-react/README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,157 @@ const { mutate } = useMutation<Task, Partial<Task>>('todo_task', 'create');
268268
// mutate expects Partial<Task>
269269
```
270270

271+
## Common Patterns
272+
273+
### Master-Detail View
274+
275+
```tsx
276+
function TaskList() {
277+
const [selectedId, setSelectedId] = useState<string | null>(null);
278+
279+
const { data: tasks } = useQuery('todo_task', {
280+
select: ['id', 'subject'],
281+
sort: ['-created_at']
282+
});
283+
284+
const { data: selectedTask } = useQuery('todo_task', {
285+
filters: ['id', '=', selectedId],
286+
enabled: !!selectedId // Only fetch when ID is selected
287+
});
288+
289+
return (
290+
<div className="flex">
291+
<TaskListPanel tasks={tasks?.value} onSelect={setSelectedId} />
292+
<TaskDetail task={selectedTask?.value?.[0]} />
293+
</div>
294+
);
295+
}
296+
```
297+
298+
### Optimistic Updates
299+
300+
```tsx
301+
function TaskToggle({ taskId, completed }) {
302+
const { mutate } = useMutation('todo_task', 'update', {
303+
onMutate: async (variables) => {
304+
// Optimistically update UI
305+
return { previousValue: completed };
306+
},
307+
onError: (error, variables, context) => {
308+
// Revert on error
309+
console.error('Update failed, reverting', context.previousValue);
310+
},
311+
onSuccess: () => {
312+
// Refetch to ensure data consistency
313+
queryClient.invalidateQueries(['todo_task']);
314+
}
315+
});
316+
317+
return (
318+
<Checkbox
319+
checked={completed}
320+
onChange={(e) => mutate({ id: taskId, is_completed: e.target.checked })}
321+
/>
322+
);
323+
}
324+
```
325+
326+
### Dependent Queries
327+
328+
```tsx
329+
function ProjectTasks({ projectId }) {
330+
// First, get project details
331+
const { data: project } = useQuery('project', {
332+
filters: ['id', '=', projectId]
333+
});
334+
335+
// Then, get tasks for this project
336+
const { data: tasks } = useQuery('todo_task', {
337+
filters: ['project_id', '=', projectId],
338+
enabled: !!project // Only fetch when project is loaded
339+
});
340+
341+
return (
342+
<div>
343+
<h2>{project?.value?.[0]?.name}</h2>
344+
<TaskList tasks={tasks?.value} />
345+
</div>
346+
);
347+
}
348+
```
349+
350+
### Search with Debounce
351+
352+
```tsx
353+
import { useDeferredValue } from 'react';
354+
355+
function TaskSearch() {
356+
const [searchTerm, setSearchTerm] = useState('');
357+
const deferredSearch = useDeferredValue(searchTerm);
358+
359+
const { data, isLoading } = useQuery('todo_task', {
360+
filters: ['subject', 'contains', deferredSearch],
361+
enabled: deferredSearch.length >= 3 // Only search with 3+ chars
362+
});
363+
364+
return (
365+
<div>
366+
<input
367+
type="text"
368+
value={searchTerm}
369+
onChange={(e) => setSearchTerm(e.target.value)}
370+
placeholder="Search tasks..."
371+
/>
372+
{isLoading && <Spinner />}
373+
<TaskList tasks={data?.value} />
374+
</div>
375+
);
376+
}
377+
```
378+
379+
### Form with Validation
380+
381+
```tsx
382+
function TaskForm() {
383+
const { mutate, isLoading, error } = useMutation('todo_task', 'create', {
384+
onSuccess: () => {
385+
router.push('/tasks');
386+
}
387+
});
388+
389+
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
390+
e.preventDefault();
391+
const formData = new FormData(e.currentTarget);
392+
393+
mutate({
394+
subject: formData.get('subject'),
395+
priority: Number(formData.get('priority')),
396+
due_date: formData.get('due_date')
397+
});
398+
};
399+
400+
return (
401+
<form onSubmit={handleSubmit}>
402+
<input name="subject" required />
403+
<input name="priority" type="number" min="1" max="5" />
404+
<input name="due_date" type="date" />
405+
406+
{error && (
407+
<div className="error">
408+
{error.code === 'validation_error'
409+
? 'Please check your input'
410+
: error.message}
411+
</div>
412+
)}
413+
414+
<button type="submit" disabled={isLoading}>
415+
{isLoading ? 'Creating...' : 'Create Task'}
416+
</button>
417+
</form>
418+
);
419+
}
420+
```
421+
271422
## License
272423

273424
Apache-2.0

packages/client/README.md

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,48 @@ try {
157157
}
158158
```
159159

160-
Common error codes:
161-
- `validation_error`: Input validation failed
162-
- `unauthenticated`: Authentication required
163-
- `permission_denied`: Insufficient permissions
164-
- `resource_not_found`: Resource does not exist
165-
- `rate_limit_exceeded`: Too many requests
160+
### Error Code Reference
161+
162+
#### Client Errors (4xx)
163+
164+
| Error Code | HTTP Status | Retryable | Description |
165+
|------------|-------------|-----------|-------------|
166+
| `validation_error` | 400 | No | Input validation failed. Check error.details for field-specific errors |
167+
| `unauthenticated` | 401 | No | Authentication required. Provide valid token |
168+
| `permission_denied` | 403 | No | Insufficient permissions for this operation |
169+
| `resource_not_found` | 404 | No | Resource does not exist. Verify object name or record ID |
170+
| `conflict` | 409 | No | Resource conflict (e.g., duplicate unique field) |
171+
| `rate_limit_exceeded` | 429 | Yes | Too many requests. Wait before retrying |
172+
173+
#### Server Errors (5xx)
174+
175+
| Error Code | HTTP Status | Retryable | Description |
176+
|------------|-------------|-----------|-------------|
177+
| `internal_error` | 500 | Yes | Server encountered an error. Retry with backoff |
178+
| `service_unavailable` | 503 | Yes | Service temporarily unavailable. Retry later |
179+
| `gateway_timeout` | 504 | Yes | Request timeout. Consider increasing timeout or retrying |
180+
181+
**Retry Strategy Example:**
182+
183+
```typescript
184+
async function retryableRequest<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
185+
for (let i = 0; i < maxRetries; i++) {
186+
try {
187+
return await fn();
188+
} catch (error: any) {
189+
if (!error.retryable || i === maxRetries - 1) {
190+
throw error;
191+
}
192+
// Exponential backoff
193+
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
194+
}
195+
}
196+
throw new Error('Max retries exceeded');
197+
}
198+
199+
// Usage
200+
const data = await retryableRequest(() =>
201+
client.data.create('todo_task', { subject: 'Task' })
202+
);
203+
```
166204

packages/core/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,42 @@ const service = kernel.getService('my-service');
8282
await kernel.shutdown();
8383
```
8484

85+
## 🤖 AI Quick Reference
86+
87+
**For AI Agents:** This package implements a microkernel architecture. Key concepts:
88+
89+
1. **Plugin Lifecycle**: `init()``start()``destroy()`
90+
2. **Service Registry**: Share functionality via `ctx.registerService(name, service)` and `ctx.getService(name)`
91+
3. **Dependencies**: Declare plugin dependencies for automatic load ordering
92+
4. **Hooks/Events**: Decouple plugins with `ctx.hook(event, handler)` and `ctx.trigger(event, ...args)`
93+
5. **Logger**: Always use `ctx.logger` for consistent, structured logging
94+
95+
**Common Plugin Pattern:**
96+
```typescript
97+
const plugin: Plugin = {
98+
name: 'my-plugin',
99+
dependencies: ['other-plugin'], // Load after these plugins
100+
101+
async init(ctx: PluginContext) {
102+
// Register services and hooks
103+
const otherService = ctx.getService('other-service');
104+
ctx.registerService('my-service', new MyService(otherService));
105+
ctx.hook('data:created', async (data) => { /* ... */ });
106+
},
107+
108+
async start(ctx: PluginContext) {
109+
// Execute business logic
110+
const service = ctx.getService('my-service');
111+
await service.initialize();
112+
},
113+
114+
async destroy() {
115+
// Cleanup resources
116+
await service.close();
117+
}
118+
};
119+
```
120+
85121
## Configurable Logger
86122

87123
The logger uses **[Pino](https://github.com/pinojs/pino)** for Node.js environments (high-performance, low-overhead) and a simple console-based logger for browsers. It automatically detects the runtime environment.

packages/spec/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,43 @@ The specification is organized into five namespaces mapping to the three-layer a
5454

5555
## 📚 Usage
5656

57+
### 🤖 AI Quick Reference
58+
59+
**For AI Agents and LLMs:** This package is the single source of truth for ObjectStack protocol definitions. When working with ObjectStack:
60+
61+
1. **Import Patterns**: Always use namespace imports (`Data.Field`, `UI.View`) or direct subpath imports
62+
2. **Validation**: Use Zod schemas for runtime validation (e.g., `ObjectSchema.parse(config)`)
63+
3. **Type Inference**: Derive TypeScript types from Zod (`type Field = z.infer<typeof FieldSchema>`)
64+
4. **Naming Convention**:
65+
- Configuration keys (TypeScript properties): `camelCase` (e.g., `maxLength`, `defaultValue`)
66+
- Machine names (data values): `snake_case` (e.g., `name: 'todo_task'`, `field: 'due_date'`)
67+
5. **Context Files**: Load prompts from `prompts/` directory for architectural understanding
68+
69+
**Common Tasks:**
70+
```typescript
71+
// Define an Object
72+
import { Data } from '@objectstack/spec';
73+
const task: Data.Object = {
74+
name: 'todo_task',
75+
fields: {
76+
subject: { type: 'text', label: 'Subject', required: true },
77+
priority: { type: 'number', min: 1, max: 5 }
78+
}
79+
};
80+
81+
// Validate at runtime
82+
import { ObjectSchema } from '@objectstack/spec/data';
83+
const result = ObjectSchema.safeParse(task);
84+
85+
// Define an App
86+
import { UI } from '@objectstack/spec';
87+
const app: UI.App = {
88+
id: 'crm',
89+
name: 'CRM',
90+
navigation: { /* ... */ }
91+
};
92+
```
93+
5794
### Import Styles
5895

5996
**Important:** This package does NOT export types at the root level to prevent naming conflicts. You must use one of the following import styles:

0 commit comments

Comments
 (0)