From d9d73c7985cd89e5b6581c643e3489dedb17dff5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:35:04 +0000 Subject: [PATCH 1/6] Initial plan From 0802a3e118afb3a2d971a926340bedec5e5cf90e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:38:30 +0000 Subject: [PATCH 2/6] Add comprehensive README for @objectstack/cli and @objectstack/types packages Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/cli/README.md | 487 +++++++++++++++++++++++++++++++++++++++ packages/types/README.md | 382 ++++++++++++++++++++++++++++++ 2 files changed, 869 insertions(+) create mode 100644 packages/cli/README.md create mode 100644 packages/types/README.md diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 000000000..9a19beacb --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,487 @@ +# @objectstack/cli + +Command Line Interface for ObjectStack Protocol - Development tools for building, serving, and managing ObjectStack applications. + +## Features + +- ๐Ÿš€ **Development Server** - Hot-reload development server with auto-discovery +- ๐Ÿ”จ **Build & Compile** - Validate and compile ObjectStack configurations to JSON +- ๐Ÿฅ **Environment Check** - Health check for development environment +- ๐Ÿ“ฆ **Project Scaffolding** - Generate plugins and examples from templates +- ๐Ÿงช **Test Runner** - Execute Quality Protocol test scenarios +- โšก **Auto-Configuration** - Smart defaults with minimal configuration + +## Installation + +### Global Installation (Recommended) + +```bash +npm install -g @objectstack/cli +# or +pnpm add -g @objectstack/cli +``` + +### Local Development + +```bash +pnpm add -D @objectstack/cli +``` + +## Commands + +### `objectstack serve` + +Start an ObjectStack server with automatic plugin loading and configuration. + +```bash +# Start server with default config (objectstack.config.ts) +objectstack serve + +# Specify custom config file +objectstack serve my-config.ts + +# Custom port +objectstack serve --port 8080 + +# Development mode (loads devPlugins) +objectstack serve --dev + +# Skip HTTP server plugin +objectstack serve --no-server +``` + +**Features:** +- Auto-detects and loads `objectstack.config.ts` +- Auto-injects ObjectQL Engine if objects are defined +- Auto-injects Memory Driver in development mode +- Auto-registers AppPlugin for app configurations +- Finds available port if requested port is in use +- Pretty logging in development mode +- Graceful shutdown on SIGINT (Ctrl+C) + +**Example:** + +```typescript +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; + +export default defineStack({ + manifest: { + name: 'my-app', + version: '1.0.0' + }, + objects: [ + { + name: 'todo_task', + fields: { + subject: { type: 'text', label: 'Subject' }, + completed: { type: 'boolean', default: false } + } + } + ] +}); +``` + +```bash +$ objectstack serve --dev + +๐Ÿš€ ObjectStack Server +------------------------ +๐Ÿ“‚ Config: objectstack.config.ts +๐ŸŒ Port: 3000 + +๐Ÿ“ฆ Loading configuration... +โœ“ Configuration loaded +๐Ÿ”ง Initializing ObjectStack kernel... + Auto-injecting ObjectQL Engine... + โœ“ Registered ObjectQL Plugin (auto-detected) + Auto-injecting Memory Driver (Dev Mode)... + โœ“ Registered Memory Driver (auto-detected) + โœ“ Registered App Plugin (auto-detected) + โœ“ Registered HTTP server plugin (port: 3000) + +๐Ÿš€ Starting ObjectStack... + +โœ… ObjectStack server is running! + Press Ctrl+C to stop +``` + +### `objectstack dev` + +Start development mode with watch and hot-reload capabilities. + +```bash +# Single package mode (if objectstack.config.ts exists in CWD) +objectstack dev + +# Monorepo mode - run dev for all packages +objectstack dev + +# Monorepo mode - filter specific package +objectstack dev @objectstack/core +``` + +**Behavior:** +- **Single Package Mode**: If `objectstack.config.ts` exists in current directory, delegates to `objectstack serve --dev` +- **Monorepo Mode**: If in workspace root, runs `pnpm dev` with optional package filter + +### `objectstack compile` + +Compile and validate ObjectStack configuration files. + +```bash +# Compile default config +objectstack compile + +# Custom source and output +objectstack compile src/config.ts build/app.json +``` + +**Features:** +- Bundles TypeScript configuration files +- Validates against ObjectStack Protocol using Zod schemas +- Outputs optimized JSON artifact +- Shows validation errors with exact field paths +- Build time and artifact size reporting + +**Example:** + +```bash +$ objectstack compile + +๐Ÿ”น ObjectStack Compiler v0.1 +------------------------------ +๐Ÿ“‚ Source: objectstack.config.ts +๐Ÿ“ฆ Bundling Configuration... +๐Ÿ” Validating Protocol Compliance... + +โœ… Build Success (234ms) +๐Ÿ“ฆ Artifact: dist/objectstack.json (12.45 KB) +โœจ Ready for Deployment +``` + +### `objectstack doctor` + +Check development environment health and dependencies. + +```bash +# Quick health check +objectstack doctor + +# Detailed information with fix suggestions +objectstack doctor --verbose +``` + +**Checks:** +- โœ“ Node.js version (>= 18.0.0 required) +- โœ“ pnpm package manager +- โœ“ TypeScript compiler +- โœ“ Dependencies installation status +- โœ“ @objectstack/spec build status +- โœ“ Git installation + +**Example Output:** + +```bash +$ objectstack doctor + +๐Ÿฅ ObjectStack Environment Health Check +----------------------------------------- + +โœ“ Node.js Version v20.10.0 +โœ“ pnpm Version 10.28.1 +โœ“ TypeScript Version 5.3.3 +โœ“ Dependencies Installed +โœ“ @objectstack/spec Built +โœ“ Git git version 2.39.0 + +โœ… Environment is healthy and ready for development! +``` + +### `objectstack create` + +Create new plugins or examples from templates. + +```bash +# Create a plugin +objectstack create plugin my-feature + +# Create an example +objectstack create example todo-app + +# Custom directory +objectstack create plugin auth --dir custom/path +``` + +**Templates:** + +#### Plugin Template +Creates a fully-configured ObjectStack plugin with: +- `package.json` with dependencies +- TypeScript configuration +- Plugin implementation boilerplate +- README with usage examples +- Test setup with Vitest + +#### Example Template +Creates an ObjectStack example application with: +- `objectstack.config.ts` configuration +- Build and dev scripts +- TypeScript configuration +- Example README + +**Example:** + +```bash +$ objectstack create plugin authentication + +๐Ÿ“ฆ ObjectStack Project Creator +------------------------------- +๐Ÿ“ Creating plugin: authentication +๐Ÿ“‚ Location: packages/plugins/plugin-authentication + +โœ“ Created package.json +โœ“ Created tsconfig.json +โœ“ Created src/index.ts +โœ“ Created README.md + +โœ… Project created successfully! + +Next steps: + cd packages/plugins/plugin-authentication + pnpm install + pnpm build +``` + +### `objectstack test:run` + +Run Quality Protocol test scenarios against your ObjectStack server. + +```bash +# Run all tests in qa/ directory +objectstack test:run + +# Specific test file or pattern +objectstack test:run qa/api-tests.json + +# Custom target URL +objectstack test:run --url http://staging.example.com + +# With authentication +objectstack test:run --token "Bearer xyz..." +``` + +**Features:** +- Executes test scenarios from JSON files +- HTTP-based test adapter +- Detailed step-by-step output +- Pass/fail reporting with timing +- Authentication support + +## Configuration File + +The CLI expects an `objectstack.config.ts` file in your project root: + +```typescript +import { defineStack } from '@objectstack/spec'; +import type { ObjectStackDefinition } from '@objectstack/spec'; + +export default defineStack({ + // App metadata + manifest: { + name: 'my-app', + version: '1.0.0', + description: 'My ObjectStack Application' + }, + + // Data objects + objects: [ + { + name: 'customer', + label: 'Customer', + fields: { + name: { type: 'text', label: 'Name', required: true }, + email: { type: 'email', label: 'Email' }, + phone: { type: 'phone', label: 'Phone' } + } + } + ], + + // UI applications + apps: [ + { + id: 'crm', + name: 'CRM', + objects: ['customer', 'opportunity'] + } + ], + + // Runtime plugins + plugins: [ + // Your production plugins + ], + + // Development-only plugins + devPlugins: [ + // Loaded only with --dev flag + ] +}); +``` + +## Usage in Monorepo + +The CLI is monorepo-aware and works seamlessly in pnpm workspaces: + +```bash +# From workspace root +pnpm --filter @objectstack/spec build +pnpm --filter my-app dev + +# Or use the CLI shorthand +objectstack dev @objectstack/spec +``` + +## Environment Variables + +```bash +# Server port (overridden by --port flag) +PORT=3000 + +# Node environment +NODE_ENV=development|production +``` + +## Aliases + +The CLI binary is available as both `objectstack` and `os`: + +```bash +objectstack serve +# or +os serve +``` + +## Development Workflow + +### New Project Setup + +```bash +# 1. Create a new example +objectstack create example my-project + +# 2. Navigate to project +cd examples/my-project + +# 3. Install dependencies +pnpm install + +# 4. Start development server +objectstack dev +``` + +### Plugin Development + +```bash +# 1. Create plugin +objectstack create plugin my-feature + +# 2. Implement plugin logic +# Edit packages/plugins/plugin-my-feature/src/index.ts + +# 3. Build and test +cd packages/plugins/plugin-my-feature +pnpm build +pnpm test +``` + +### Production Build + +```bash +# 1. Compile configuration +objectstack compile + +# 2. Run production server +NODE_ENV=production objectstack serve --port 8080 +``` + +## Integration with Package Scripts + +Add CLI commands to your `package.json`: + +```json +{ + "scripts": { + "build": "objectstack compile", + "dev": "objectstack dev", + "serve": "objectstack serve", + "test": "objectstack test:run", + "doctor": "objectstack doctor" + } +} +``` + +## Troubleshooting + +### Port Already in Use + +The CLI automatically finds an available port if the requested port is in use: + +```bash +๐ŸŒ Port: 3001 (requested: 3000 in use) +``` + +### Configuration Not Found + +Ensure your config file exists and is properly named: + +```bash +โŒ Configuration file not found: /path/to/objectstack.config.ts +``` + +Default file name: `objectstack.config.ts` + +### Validation Errors + +The compiler will show detailed validation errors: + +```bash +โŒ Validation Failed! + - [objects.0.name] Expected snake_case, received "MyObject" + - [objects.0.fields.name.type] Invalid enum value. Expected 'text' | 'number' | ... +``` + +### Environment Issues + +Run the doctor command to diagnose: + +```bash +objectstack doctor --verbose +``` + +## API Reference + +### Command Options + +All commands support `--help` for detailed usage: + +```bash +objectstack --help +objectstack serve --help +objectstack compile --help +``` + +### Exit Codes + +- `0` - Success +- `1` - Error (validation failed, file not found, etc.) + +## Related Packages + +- [@objectstack/spec](../spec) - Protocol definitions and schemas +- [@objectstack/core](../core) - Microkernel runtime +- [@objectstack/objectql](../objectql) - Data query engine +- [@objectstack/runtime](../runtime) - Runtime utilities and plugins + +## License + +MIT diff --git a/packages/types/README.md b/packages/types/README.md new file mode 100644 index 000000000..4718fd447 --- /dev/null +++ b/packages/types/README.md @@ -0,0 +1,382 @@ +# @objectstack/types + +Shared runtime type definitions for the ObjectStack ecosystem. + +## Overview + +This package provides common TypeScript interfaces and types used across the ObjectStack runtime environment. It serves as a minimal, dependency-light package that defines the core contracts between different parts of the system. + +## Purpose + +The `@objectstack/types` package exists to: + +1. **Break Circular Dependencies** - Provides shared types without creating circular imports between packages +2. **Define Runtime Contracts** - Establishes interfaces that plugins and kernel must satisfy +3. **Enable Type Safety** - Ensures type compatibility across the ObjectStack ecosystem +4. **Minimal Footprint** - Ultra-lightweight with minimal dependencies + +## Installation + +```bash +pnpm add @objectstack/types +``` + +## Type Definitions + +### IKernel + +Core interface for the ObjectStack kernel that plugins interact with. + +```typescript +export interface IKernel { + /** + * ObjectQL instance (optional to support initialization phase) + */ + ql?: any; + + /** + * Start the kernel and all registered plugins + */ + start(): Promise; + + /** + * Additional kernel methods and services + */ + [key: string]: any; +} +``` + +**Usage:** + +```typescript +import type { IKernel } from '@objectstack/types'; + +export class MyPlugin { + async onStart(kernel: IKernel) { + // Access ObjectQL if available + if (kernel.ql) { + await kernel.ql.find('user', {}); + } + } +} +``` + +### RuntimeContext + +Context object passed to runtime plugins during their lifecycle. + +```typescript +export interface RuntimeContext { + /** + * Reference to the kernel instance + */ + engine: IKernel; +} +``` + +**Usage:** + +```typescript +import type { RuntimeContext } from '@objectstack/types'; + +export const myPlugin = { + name: 'my-plugin', + + async install(ctx: RuntimeContext) { + // Use kernel through context + await ctx.engine.start(); + } +}; +``` + +### RuntimePlugin + +Interface for plugins that integrate with the ObjectStack runtime. + +```typescript +export interface RuntimePlugin { + /** + * Unique plugin identifier + */ + name: string; + + /** + * Optional: Called when plugin is registered + */ + install?: (ctx: RuntimeContext) => void | Promise; + + /** + * Optional: Called when kernel starts + */ + onStart?: (ctx: RuntimeContext) => void | Promise; +} +``` + +**Usage:** + +```typescript +import type { RuntimePlugin, RuntimeContext } from '@objectstack/types'; + +export const myPlugin: RuntimePlugin = { + name: 'my-custom-plugin', + + async install(ctx: RuntimeContext) { + console.log('Plugin installed'); + // Register services, hooks, etc. + }, + + async onStart(ctx: RuntimeContext) { + console.log('Plugin started'); + // Initialize connections, start background tasks, etc. + } +}; +``` + +## Architecture + +### Design Philosophy + +The `@objectstack/types` package follows these principles: + +1. **Interface Segregation** - Only define what's absolutely necessary +2. **Loose Coupling** - Enable interoperability without tight dependencies +3. **Progressive Enhancement** - Allow plugins to implement optional interfaces +4. **Type Safety** - Provide compile-time guarantees where possible + +### Dependency Graph + +``` +@objectstack/types (Layer 0) +โ”œโ”€โ”€ Dependencies: @objectstack/spec (protocols only) +โ””โ”€โ”€ Used By: + โ”œโ”€โ”€ @objectstack/core (kernel implementation) + โ”œโ”€โ”€ @objectstack/runtime (plugin utilities) + โ”œโ”€โ”€ @objectstack/objectql (query engine) + โ””โ”€โ”€ All plugins +``` + +The types package sits at the foundation of the architecture, depended upon by nearly all other packages but depending on very few itself. + +## Use Cases + +### 1. Plugin Development + +When building plugins, import types for proper typing: + +```typescript +import type { RuntimePlugin, RuntimeContext } from '@objectstack/types'; + +export const authPlugin: RuntimePlugin = { + name: 'auth', + + async install(ctx: RuntimeContext) { + // Setup authentication + }, + + async onStart(ctx: RuntimeContext) { + // Start auth service + } +}; +``` + +### 2. Kernel Extensions + +When extending the kernel with custom functionality: + +```typescript +import type { IKernel } from '@objectstack/types'; + +export class CustomKernel implements IKernel { + ql?: any; + + async start(): Promise { + // Custom kernel implementation + } +} +``` + +### 3. Runtime Integration + +When integrating with the ObjectStack runtime: + +```typescript +import type { RuntimeContext } from '@objectstack/types'; + +export function setupRuntime(ctx: RuntimeContext) { + const kernel = ctx.engine; + + // Access kernel functionality + kernel.start(); +} +``` + +## TypeScript Configuration + +This package is designed to work with TypeScript 5.0+: + +```json +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true + } +} +``` + +## Best Practices + +### 1. Use Type-Only Imports + +When importing types, use the `type` keyword to ensure they're erased at runtime: + +```typescript +import type { IKernel, RuntimePlugin } from '@objectstack/types'; +``` + +### 2. Don't Import Implementation + +This package contains **only types**. Never import implementation details: + +```typescript +// โœ… Good - type-only import +import type { RuntimePlugin } from '@objectstack/types'; + +// โŒ Bad - trying to import implementation +import { RuntimePlugin } from '@objectstack/types'; // Won't work +``` + +### 3. Extend Interfaces When Needed + +Plugins can extend these interfaces for custom requirements: + +```typescript +import type { RuntimePlugin, RuntimeContext } from '@objectstack/types'; + +interface MyPluginContext extends RuntimeContext { + customProperty: string; +} + +export const myPlugin: RuntimePlugin = { + name: 'my-plugin', + + async install(ctx: RuntimeContext) { + const myCtx = ctx as MyPluginContext; + // Use custom context + } +}; +``` + +### 4. Maintain Backward Compatibility + +The types in this package should be stable. Use optional properties for new features: + +```typescript +export interface RuntimePlugin { + name: string; + install?: (ctx: RuntimeContext) => void | Promise; + onStart?: (ctx: RuntimeContext) => void | Promise; + // New optional methods are OK + onDestroy?: () => void | Promise; +} +``` + +## Version Compatibility + +This package follows semantic versioning: + +- **Patch**: Documentation updates, internal refactoring +- **Minor**: New optional properties/methods +- **Major**: Breaking changes to existing interfaces + +## FAQ + +### Why a separate types package? + +To avoid circular dependencies between `@objectstack/core` and plugins, we extract shared interfaces into a neutral package. + +### Can I use this in my plugin? + +Yes! This package is designed to be used by all ObjectStack plugins and extensions. + +### Do I need this for application development? + +Usually not. Application developers typically use `@objectstack/spec` for type definitions. This package is primarily for plugin and runtime developers. + +### What's the difference from @objectstack/spec? + +- **@objectstack/spec**: Protocol definitions, schemas, and data types (what you configure) +- **@objectstack/types**: Runtime interfaces and contracts (how the system runs) + +## Migration Guide + +### From Direct Kernel Access + +Before: + +```typescript +import { ObjectKernel } from '@objectstack/core'; + +function myFunction(kernel: ObjectKernel) { + // Tight coupling to concrete class +} +``` + +After: + +```typescript +import type { IKernel } from '@objectstack/types'; + +function myFunction(kernel: IKernel) { + // Loose coupling to interface +} +``` + +### From Inline Types + +Before: + +```typescript +interface MyPlugin { + name: string; + install: (ctx: any) => Promise; +} +``` + +After: + +```typescript +import type { RuntimePlugin } from '@objectstack/types'; + +const myPlugin: RuntimePlugin = { + name: 'my-plugin', + install: async (ctx) => { + // Implementation + } +}; +``` + +## Related Packages + +- [@objectstack/spec](../spec) - Protocol definitions and Zod schemas +- [@objectstack/core](../core) - Kernel implementation +- [@objectstack/runtime](../runtime) - Runtime utilities and helpers + +## Contributing + +When adding new types to this package: + +1. Keep interfaces minimal and focused +2. Use optional properties for extensibility +3. Document all public types with JSDoc +4. Avoid breaking changes +5. Consider backward compatibility + +## License + +MIT From 0c7e6e140e4da6943f3d1286a963c352d1b66721 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:40:00 +0000 Subject: [PATCH 3/6] Enhance READMEs with AI quick reference and practical examples Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/client-react/README.md | 151 ++++++++++++++++++++++++++++++++ packages/client/README.md | 50 +++++++++-- packages/core/README.md | 36 ++++++++ packages/spec/README.md | 37 ++++++++ 4 files changed, 268 insertions(+), 6 deletions(-) diff --git a/packages/client-react/README.md b/packages/client-react/README.md index 32228c586..b2a3a1ccd 100644 --- a/packages/client-react/README.md +++ b/packages/client-react/README.md @@ -268,6 +268,157 @@ const { mutate } = useMutation>('todo_task', 'create'); // mutate expects Partial ``` +## Common Patterns + +### Master-Detail View + +```tsx +function TaskList() { + const [selectedId, setSelectedId] = useState(null); + + const { data: tasks } = useQuery('todo_task', { + select: ['id', 'subject'], + sort: ['-created_at'] + }); + + const { data: selectedTask } = useQuery('todo_task', { + filters: ['id', '=', selectedId], + enabled: !!selectedId // Only fetch when ID is selected + }); + + return ( +
+ + +
+ ); +} +``` + +### Optimistic Updates + +```tsx +function TaskToggle({ taskId, completed }) { + const { mutate } = useMutation('todo_task', 'update', { + onMutate: async (variables) => { + // Optimistically update UI + return { previousValue: completed }; + }, + onError: (error, variables, context) => { + // Revert on error + console.error('Update failed, reverting', context.previousValue); + }, + onSuccess: () => { + // Refetch to ensure data consistency + queryClient.invalidateQueries(['todo_task']); + } + }); + + return ( + mutate({ id: taskId, is_completed: e.target.checked })} + /> + ); +} +``` + +### Dependent Queries + +```tsx +function ProjectTasks({ projectId }) { + // First, get project details + const { data: project } = useQuery('project', { + filters: ['id', '=', projectId] + }); + + // Then, get tasks for this project + const { data: tasks } = useQuery('todo_task', { + filters: ['project_id', '=', projectId], + enabled: !!project // Only fetch when project is loaded + }); + + return ( +
+

{project?.value?.[0]?.name}

+ +
+ ); +} +``` + +### Search with Debounce + +```tsx +import { useDeferredValue } from 'react'; + +function TaskSearch() { + const [searchTerm, setSearchTerm] = useState(''); + const deferredSearch = useDeferredValue(searchTerm); + + const { data, isLoading } = useQuery('todo_task', { + filters: ['subject', 'contains', deferredSearch], + enabled: deferredSearch.length >= 3 // Only search with 3+ chars + }); + + return ( +
+ setSearchTerm(e.target.value)} + placeholder="Search tasks..." + /> + {isLoading && } + +
+ ); +} +``` + +### Form with Validation + +```tsx +function TaskForm() { + const { mutate, isLoading, error } = useMutation('todo_task', 'create', { + onSuccess: () => { + router.push('/tasks'); + } + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + const formData = new FormData(e.currentTarget); + + mutate({ + subject: formData.get('subject'), + priority: Number(formData.get('priority')), + due_date: formData.get('due_date') + }); + }; + + return ( +
+ + + + + {error && ( +
+ {error.code === 'validation_error' + ? 'Please check your input' + : error.message} +
+ )} + + +
+ ); +} +``` + ## License Apache-2.0 diff --git a/packages/client/README.md b/packages/client/README.md index f14383c15..b3bbbfaab 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -157,10 +157,48 @@ try { } ``` -Common error codes: -- `validation_error`: Input validation failed -- `unauthenticated`: Authentication required -- `permission_denied`: Insufficient permissions -- `resource_not_found`: Resource does not exist -- `rate_limit_exceeded`: Too many requests +### Error Code Reference + +#### Client Errors (4xx) + +| Error Code | HTTP Status | Retryable | Description | +|------------|-------------|-----------|-------------| +| `validation_error` | 400 | No | Input validation failed. Check error.details for field-specific errors | +| `unauthenticated` | 401 | No | Authentication required. Provide valid token | +| `permission_denied` | 403 | No | Insufficient permissions for this operation | +| `resource_not_found` | 404 | No | Resource does not exist. Verify object name or record ID | +| `conflict` | 409 | No | Resource conflict (e.g., duplicate unique field) | +| `rate_limit_exceeded` | 429 | Yes | Too many requests. Wait before retrying | + +#### Server Errors (5xx) + +| Error Code | HTTP Status | Retryable | Description | +|------------|-------------|-----------|-------------| +| `internal_error` | 500 | Yes | Server encountered an error. Retry with backoff | +| `service_unavailable` | 503 | Yes | Service temporarily unavailable. Retry later | +| `gateway_timeout` | 504 | Yes | Request timeout. Consider increasing timeout or retrying | + +**Retry Strategy Example:** + +```typescript +async function retryableRequest(fn: () => Promise, maxRetries = 3): Promise { + for (let i = 0; i < maxRetries; i++) { + try { + return await fn(); + } catch (error: any) { + if (!error.retryable || i === maxRetries - 1) { + throw error; + } + // Exponential backoff + await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000)); + } + } + throw new Error('Max retries exceeded'); +} + +// Usage +const data = await retryableRequest(() => + client.data.create('todo_task', { subject: 'Task' }) +); +``` diff --git a/packages/core/README.md b/packages/core/README.md index 7d375689b..291f758b4 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -82,6 +82,42 @@ const service = kernel.getService('my-service'); await kernel.shutdown(); ``` +## ๐Ÿค– AI Quick Reference + +**For AI Agents:** This package implements a microkernel architecture. Key concepts: + +1. **Plugin Lifecycle**: `init()` โ†’ `start()` โ†’ `destroy()` +2. **Service Registry**: Share functionality via `ctx.registerService(name, service)` and `ctx.getService(name)` +3. **Dependencies**: Declare plugin dependencies for automatic load ordering +4. **Hooks/Events**: Decouple plugins with `ctx.hook(event, handler)` and `ctx.trigger(event, ...args)` +5. **Logger**: Always use `ctx.logger` for consistent, structured logging + +**Common Plugin Pattern:** +```typescript +const plugin: Plugin = { + name: 'my-plugin', + dependencies: ['other-plugin'], // Load after these plugins + + async init(ctx: PluginContext) { + // Register services and hooks + const otherService = ctx.getService('other-service'); + ctx.registerService('my-service', new MyService(otherService)); + ctx.hook('data:created', async (data) => { /* ... */ }); + }, + + async start(ctx: PluginContext) { + // Execute business logic + const service = ctx.getService('my-service'); + await service.initialize(); + }, + + async destroy() { + // Cleanup resources + await service.close(); + } +}; +``` + ## Configurable Logger 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. diff --git a/packages/spec/README.md b/packages/spec/README.md index f9f55fcea..0a1690d56 100644 --- a/packages/spec/README.md +++ b/packages/spec/README.md @@ -54,6 +54,43 @@ The specification is organized into five namespaces mapping to the three-layer a ## ๐Ÿ“š Usage +### ๐Ÿค– AI Quick Reference + +**For AI Agents and LLMs:** This package is the single source of truth for ObjectStack protocol definitions. When working with ObjectStack: + +1. **Import Patterns**: Always use namespace imports (`Data.Field`, `UI.View`) or direct subpath imports +2. **Validation**: Use Zod schemas for runtime validation (e.g., `ObjectSchema.parse(config)`) +3. **Type Inference**: Derive TypeScript types from Zod (`type Field = z.infer`) +4. **Naming Convention**: + - Configuration keys (TypeScript properties): `camelCase` (e.g., `maxLength`, `defaultValue`) + - Machine names (data values): `snake_case` (e.g., `name: 'todo_task'`, `field: 'due_date'`) +5. **Context Files**: Load prompts from `prompts/` directory for architectural understanding + +**Common Tasks:** +```typescript +// Define an Object +import { Data } from '@objectstack/spec'; +const task: Data.Object = { + name: 'todo_task', + fields: { + subject: { type: 'text', label: 'Subject', required: true }, + priority: { type: 'number', min: 1, max: 5 } + } +}; + +// Validate at runtime +import { ObjectSchema } from '@objectstack/spec/data'; +const result = ObjectSchema.safeParse(task); + +// Define an App +import { UI } from '@objectstack/spec'; +const app: UI.App = { + id: 'crm', + name: 'CRM', + navigation: { /* ... */ } +}; +``` + ### Import Styles **Important:** This package does NOT export types at the root level to prevent naming conflicts. You must use one of the following import styles: From e9984a323ab57b3f45bd47f6eef1426c2330aafa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:41:52 +0000 Subject: [PATCH 4/6] Add comprehensive workflow examples and plugin patterns to package READMEs Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/metadata/README.md | 238 +++++++++++++++++++++++++++++ packages/objectql/README.md | 269 +++++++++++++++++++++++++++++++++ packages/runtime/README.md | 293 ++++++++++++++++++++++++++++++++++++ 3 files changed, 800 insertions(+) diff --git a/packages/metadata/README.md b/packages/metadata/README.md index da4366b7d..b5b6db4aa 100644 --- a/packages/metadata/README.md +++ b/packages/metadata/README.md @@ -196,6 +196,244 @@ The metadata package is designed as a Layer 3 package in the ObjectStack archite โ””โ”€โ”€ @objectstack/objectql (registry persistence) ``` +## Common Workflows + +### Development Workflow with File Watching + +```typescript +import { MetadataManager } from '@objectstack/metadata'; + +const manager = new MetadataManager({ + rootDir: './metadata', + watch: true, // Enable file watching + cache: { enabled: true, ttl: 3600 } +}); + +// Watch for changes and reload +manager.watch('object', async (event) => { + if (event.type === 'modified' || event.type === 'added') { + console.log(`Reloading object: ${event.name}`); + const updated = await manager.load('object', event.name); + + // Notify the system to reload + await objectQL.reloadObject(event.name, updated); + } +}); + +// Hot module replacement for development +console.log('Watching metadata files for changes...'); +``` + +### Metadata Migration Workflow + +```typescript +import { MetadataManager } from '@objectstack/metadata'; + +async function migrateMetadata() { + const manager = new MetadataManager({ + rootDir: './metadata' + }); + + // 1. Load all existing objects + const objects = await manager.loadMany('object'); + + // 2. Transform metadata (e.g., rename field) + const transformed = objects.map(obj => ({ + ...obj, + fields: Object.entries(obj.fields).reduce((acc, [key, field]) => { + // Rename 'description' to 'notes' + const newKey = key === 'description' ? 'notes' : key; + acc[newKey] = field; + return acc; + }, {}) + })); + + // 3. Save with backup + for (const obj of transformed) { + await manager.save('object', obj.name, obj, { + format: 'typescript', + backup: true, // Create .bak file + prettify: true + }); + } + + console.log(`Migrated ${objects.length} objects`); +} +``` + +### Multi-Format Support Workflow + +```typescript +import { MetadataManager } from '@objectstack/metadata'; + +const manager = new MetadataManager({ + rootDir: './metadata', + formats: ['typescript', 'json', 'yaml'] +}); + +// Load from any format - manager auto-detects +const customer = await manager.load('object', 'customer'); +// Tries: customer.object.ts, customer.object.json, customer.object.yaml + +// Save in preferred format +await manager.save('object', 'customer', customer, { + format: 'typescript' // Convert to TypeScript +}); + +// Generate documentation from metadata +const allObjects = await manager.loadMany('object'); +const docs = allObjects.map(obj => ` +## ${obj.label} + +**Name:** ${obj.name} + +**Fields:** +${Object.entries(obj.fields).map(([name, field]) => + `- **${field.label}** (\`${name}\`): ${field.type}` +).join('\n')} +`).join('\n\n'); + +fs.writeFileSync('docs/objects.md', docs); +``` + +### Validation and Testing Workflow + +```typescript +import { MetadataManager } from '@objectstack/metadata'; +import { ObjectSchema } from '@objectstack/spec/data'; + +async function validateAllMetadata() { + const manager = new MetadataManager({ + rootDir: './metadata' + }); + + const objects = await manager.loadMany('object'); + const errors = []; + + for (const obj of objects) { + const result = ObjectSchema.safeParse(obj); + + if (!result.success) { + errors.push({ + name: obj.name, + issues: result.error.issues + }); + } + } + + if (errors.length > 0) { + console.error('Validation errors found:'); + errors.forEach(({ name, issues }) => { + console.error(`\n${name}:`); + issues.forEach(issue => { + console.error(` - ${issue.path.join('.')}: ${issue.message}`); + }); + }); + process.exit(1); + } + + console.log(`โœ… All ${objects.length} objects validated successfully`); +} +``` + +### Metadata Versioning Workflow + +```typescript +import { MetadataManager } from '@objectstack/metadata'; +import { execSync } from 'child_process'; + +async function versionMetadata() { + const manager = new MetadataManager({ + rootDir: './metadata' + }); + + const objects = await manager.loadMany('object'); + + // Add version metadata + const versioned = objects.map(obj => ({ + ...obj, + metadata: { + ...obj.metadata, + version: '2.0.0', + lastModified: new Date().toISOString(), + modifiedBy: execSync('git config user.name').toString().trim() + } + })); + + // Save versioned metadata + for (const obj of versioned) { + await manager.save('object', obj.name, obj, { + format: 'typescript', + prettify: true + }); + } + + // Commit to version control + execSync('git add metadata/'); + execSync('git commit -m "Version bump to 2.0.0"'); +} +``` + +### Import/Export Workflow + +```typescript +import { MetadataManager } from '@objectstack/metadata'; + +async function exportToJSON() { + const manager = new MetadataManager({ + rootDir: './metadata' + }); + + // Load all metadata + const [objects, views, apps] = await Promise.all([ + manager.loadMany('object'), + manager.loadMany('view'), + manager.loadMany('app') + ]); + + // Create unified export + const exportData = { + version: '1.0.0', + exported: new Date().toISOString(), + objects, + views, + apps + }; + + // Save as single JSON file + fs.writeFileSync( + 'export/metadata-export.json', + JSON.stringify(exportData, null, 2) + ); + + console.log('Export complete!'); +} + +async function importFromJSON(filePath: string) { + const manager = new MetadataManager({ + rootDir: './metadata' + }); + + const importData = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + + // Import objects + for (const obj of importData.objects) { + await manager.save('object', obj.name, obj, { + format: 'typescript' + }); + } + + // Import views + for (const view of importData.views) { + await manager.save('view', view.name, view, { + format: 'typescript' + }); + } + + console.log('Import complete!'); +} +``` + ## License MIT diff --git a/packages/objectql/README.md b/packages/objectql/README.md index 02a22c1b8..f44831d8f 100644 --- a/packages/objectql/README.md +++ b/packages/objectql/README.md @@ -91,6 +91,275 @@ await kernel.bootstrap(); - **QueryPlanner**: (Internal) Normalizes simplified queries into `QueryAST`. - **Executor**: Routes AST to the correct driver. +## Advanced Examples + +### Complex Queries + +```typescript +// Filtering with multiple conditions +const activeTasks = await ql.find('todo_task', { + where: { + $and: [ + { status: 'active' }, + { priority: { $gte: 3 } }, + { due_date: { $lte: new Date() } } + ] + }, + orderBy: [ + { field: 'priority', order: 'desc' }, + { field: 'due_date', order: 'asc' } + ], + limit: 50 +}); + +// Text search +const searchResults = await ql.find('todo_task', { + where: { + $or: [ + { title: { $contains: 'urgent' } }, + { description: { $contains: 'urgent' } } + ] + } +}); + +// Nested conditions +const complexQuery = await ql.find('project', { + where: { + $and: [ + { status: { $in: ['active', 'planning'] } }, + { + $or: [ + { budget: { $gt: 100000 } }, + { priority: { $eq: 1 } } + ] + } + ] + } +}); +``` + +### Batch Operations + +```typescript +// Batch insert +const newTasks = [ + { title: 'Task 1', priority: 1 }, + { title: 'Task 2', priority: 2 }, + { title: 'Task 3', priority: 3 } +]; + +for (const task of newTasks) { + await ql.insert('todo_task', task); +} + +// Batch update +const tasksToUpdate = await ql.find('todo_task', { + where: { status: 'pending' } +}); + +for (const task of tasksToUpdate) { + await ql.update('todo_task', task.id, { + status: 'in_progress', + started_at: new Date() + }); +} +``` + +### Working with Relationships + +```typescript +// One-to-Many: Get project with tasks +const project = await ql.find('project', { + where: { id: 'proj_123' }, + include: ['tasks'] // Assumes 'tasks' is a relation field +}); + +// Many-to-One: Get task with project details +const task = await ql.find('todo_task', { + where: { id: 'task_456' }, + include: ['project'] +}); + +// Manual join (when driver doesn't support relations) +const tasks = await ql.find('todo_task', { + where: { project_id: 'proj_123' } +}); + +const project = await ql.find('project', { + where: { id: 'proj_123' } +}); + +const projectWithTasks = { + ...project[0], + tasks: tasks +}; +``` + +### Aggregations + +```typescript +// Count records +const totalTasks = await ql.count('todo_task', { + where: { status: 'active' } +}); + +// Sum (if driver supports) +const totalBudget = await ql.aggregate('project', { + operation: 'sum', + field: 'budget', + where: { status: 'active' } +}); + +// Group by (if driver supports) +const tasksByStatus = await ql.aggregate('todo_task', { + operation: 'count', + groupBy: ['status'] +}); +``` + +### Multi-Datasource Queries + +```typescript +import { PostgresDriver } from '@objectstack/driver-postgres'; +import { MongoDBDriver } from '@objectstack/driver-mongodb'; +import { RedisDriver } from '@objectstack/driver-redis'; + +const ql = new ObjectQL(); + +// Register multiple drivers +const pgDriver = new PostgresDriver({ connectionString: 'postgres://...' }); +const mongoDriver = new MongoDBDriver({ url: 'mongodb://...' }); +const redisDriver = new RedisDriver({ host: 'localhost' }); + +ql.registerDriver(pgDriver, false, 'postgres'); +ql.registerDriver(mongoDriver, false, 'mongodb'); +ql.registerDriver(redisDriver, false, 'redis'); + +// Configure objects to use different datasources +await ql.use({ + name: 'multi-db-app', + objects: [ + { + name: 'user', + datasource: 'postgres', // Users in PostgreSQL + fields: { /* ... */ } + }, + { + name: 'product', + datasource: 'mongodb', // Products in MongoDB + fields: { /* ... */ } + }, + { + name: 'session', + datasource: 'redis', // Sessions in Redis + fields: { /* ... */ } + } + ] +}); + +await ql.init(); + +// Query automatically routes to correct datasource +const users = await ql.find('user'); // โ†’ PostgreSQL +const products = await ql.find('product'); // โ†’ MongoDB +const sessions = await ql.find('session'); // โ†’ Redis +``` + +### Custom Hooks and Middleware + +```typescript +// Register hooks for data operations +ql.registerHook('beforeInsert', async (ctx) => { + console.log(`Creating ${ctx.object}:`, ctx.data); + + // Add timestamps + ctx.data.created_at = new Date(); + ctx.data.updated_at = new Date(); + + // Validate + if (ctx.object === 'user' && !ctx.data.email) { + throw new Error('Email is required'); + } +}); + +ql.registerHook('afterInsert', async (ctx) => { + console.log(`Created ${ctx.object}:`, ctx.result); + + // Trigger events + await eventBus.emit('data:created', { + object: ctx.object, + id: ctx.result.id + }); +}); + +ql.registerHook('beforeUpdate', async (ctx) => { + // Update timestamp + ctx.data.updated_at = new Date(); +}); + +ql.registerHook('beforeDelete', async (ctx) => { + // Soft delete + if (ctx.object === 'user') { + throw new Error('Cannot delete users, use deactivate instead'); + } +}); +``` + +### Transaction Support + +```typescript +// If driver supports transactions +const driver = ql.getDriver('default'); + +if (driver.supports.transactions) { + await driver.beginTransaction(); + + try { + await ql.insert('order', { + customer_id: 'cust_123', + total: 100.00 + }); + + await ql.update('inventory', 'inv_456', { + quantity: { $decrement: 1 } + }); + + await driver.commitTransaction(); + } catch (error) { + await driver.rollbackTransaction(); + throw error; + } +} +``` + +### Schema Migration + +```typescript +// Add new field to existing object +const currentSchema = ql.getSchema('todo_task'); + +const updatedSchema = { + ...currentSchema, + fields: { + ...currentSchema.fields, + assignee: { + type: 'lookup', + reference: 'user', + label: 'Assignee' + } + } +}; + +// Re-register schema +ql.registerObject(updatedSchema); + +// Driver might need to sync schema +const driver = ql.getDriver('default'); +if (driver.syncSchema) { + await driver.syncSchema('todo_task', updatedSchema); +} +``` + ## Roadmap - [x] Basic CRUD diff --git a/packages/runtime/README.md b/packages/runtime/README.md index 3656206cc..607707f7b 100644 --- a/packages/runtime/README.md +++ b/packages/runtime/README.md @@ -268,6 +268,299 @@ See the `examples/` directory for complete examples: 4. **Use hooks**: Decouple plugins with event system 5. **Handle errors**: Implement proper error handling in lifecycle methods +## Common Plugin Patterns + +### Service Provider Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class DatabasePlugin implements Plugin { + name = 'database'; + private connection: any; + + async init(ctx: PluginContext) { + // Initialize connection + this.connection = await createConnection({ + host: 'localhost', + database: 'myapp' + }); + + // Register as service + ctx.registerService('database', this.connection); + + ctx.logger.info('Database connected'); + } + + async destroy() { + // Cleanup + await this.connection.close(); + } +} +``` + +### Service Consumer Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class RepositoryPlugin implements Plugin { + name = 'repository'; + dependencies = ['database']; // Ensure database loads first + + async init(ctx: PluginContext) { + // Get dependency + const db = ctx.getService('database'); + + // Create and register repository + const repo = new UserRepository(db); + ctx.registerService('user-repository', repo); + } +} +``` + +### Event-Driven Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +// Publisher +export class DataPlugin implements Plugin { + name = 'data'; + + async init(ctx: PluginContext) { + const service = { + async create(entity: string, data: any) { + const result = await db.insert(entity, data); + + // Trigger event + await ctx.trigger('data:created', { entity, data: result }); + + return result; + } + }; + + ctx.registerService('data', service); + } +} + +// Subscriber +export class AuditPlugin implements Plugin { + name = 'audit'; + dependencies = ['data']; + + async init(ctx: PluginContext) { + // Listen to events + ctx.hook('data:created', async ({ entity, data }) => { + ctx.logger.info(`Audit: ${entity} created`, { id: data.id }); + + await auditLog.write({ + action: 'create', + entity, + entityId: data.id, + timestamp: new Date() + }); + }); + } +} +``` + +### Configuration Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; +import { z } from 'zod'; + +const ConfigSchema = z.object({ + apiKey: z.string(), + endpoint: z.string().url(), + timeout: z.number().default(5000) +}); + +export class ApiPlugin implements Plugin { + name = 'api-client'; + + constructor(private config: z.infer) { + // Validate config + ConfigSchema.parse(config); + } + + async init(ctx: PluginContext) { + const client = new ApiClient({ + apiKey: this.config.apiKey, + endpoint: this.config.endpoint, + timeout: this.config.timeout + }); + + ctx.registerService('api-client', client); + } +} + +// Usage +kernel.use(new ApiPlugin({ + apiKey: process.env.API_KEY, + endpoint: 'https://api.example.com', + timeout: 10000 +})); +``` + +### Factory Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class ConnectionPoolPlugin implements Plugin { + name = 'connection-pool'; + + async init(ctx: PluginContext) { + const pool = { + connections: new Map(), + + getConnection(name: string) { + if (!this.connections.has(name)) { + this.connections.set(name, createConnection(name)); + } + return this.connections.get(name); + }, + + closeAll() { + for (const conn of this.connections.values()) { + conn.close(); + } + this.connections.clear(); + } + }; + + ctx.registerService('connection-pool', pool); + } + + async destroy() { + const pool = kernel.getService('connection-pool'); + pool.closeAll(); + } +} +``` + +### Middleware Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class LoggingMiddleware implements Plugin { + name = 'logging-middleware'; + dependencies = ['http-server']; + + async start(ctx: PluginContext) { + const server = ctx.getService('http-server'); + + // Register middleware + server.use(async (req, res, next) => { + const start = Date.now(); + + ctx.logger.info('Request', { + method: req.method, + path: req.path + }); + + await next(); + + const duration = Date.now() - start; + ctx.logger.info('Response', { + method: req.method, + path: req.path, + status: res.statusCode, + duration + }); + }); + } +} +``` + +### Lazy Loading Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class HeavyServicePlugin implements Plugin { + name = 'heavy-service'; + private instance: any = null; + + async init(ctx: PluginContext) { + // Register factory instead of instance + const factory = { + async getInstance() { + if (!this.instance) { + ctx.logger.info('Lazy loading heavy service...'); + this.instance = await loadHeavyService(); + } + return this.instance; + } + }; + + ctx.registerService('heavy-service', factory); + } +} + +// Usage +const factory = kernel.getService('heavy-service'); +const service = await factory.getInstance(); // Loaded only when needed +``` + +### Health Check Pattern + +```typescript +import { Plugin, PluginContext } from '@objectstack/core'; + +export class HealthCheckPlugin implements Plugin { + name = 'health-check'; + dependencies = ['http-server', 'database', 'cache']; + + async start(ctx: PluginContext) { + const server = ctx.getService('http-server'); + + server.get('/health', async (req, res) => { + const checks = await Promise.all([ + this.checkDatabase(ctx), + this.checkCache(ctx), + this.checkDiskSpace() + ]); + + const healthy = checks.every(c => c.healthy); + + res.status(healthy ? 200 : 503).json({ + status: healthy ? 'healthy' : 'unhealthy', + checks + }); + }); + } + + private async checkDatabase(ctx: PluginContext) { + try { + const db = ctx.getService('database'); + await db.ping(); + return { name: 'database', healthy: true }; + } catch (error) { + return { name: 'database', healthy: false, error: error.message }; + } + } + + private async checkCache(ctx: PluginContext) { + try { + const cache = ctx.getService('cache'); + await cache.ping(); + return { name: 'cache', healthy: true }; + } catch (error) { + return { name: 'cache', healthy: false, error: error.message }; + } + } + + private async checkDiskSpace() { + // Implementation + return { name: 'disk', healthy: true }; + } +} +``` + ## Documentation - [MiniKernel Guide](../../MINI_KERNEL_GUIDE.md) - Complete API documentation and patterns From 49f3942b8e95543c5408e1facf20d73eebb052ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:42:41 +0000 Subject: [PATCH 5/6] Enhance root README with improved package navigation and quick links Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- README.md | 64 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 8612d3d51..8790a682d 100644 --- a/README.md +++ b/README.md @@ -132,40 +132,60 @@ See **[DEVELOPMENT.md](./DEVELOPMENT.md)** for comprehensive development guide i ### Core Packages -| Package | Description | Status | -| :--- | :--- | :--- | -| **[`@objectstack/spec`](packages/spec)** | Protocol definitions (Zod schemas, Types, JSON Schemas) - The "Constitution" | ๐ŸŸข Active | -| **[`@objectstack/core`](packages/core)** | Microkernel runtime (Plugin system, DI, Event Bus, Logger) | ๐ŸŸข Active | -| **[`@objectstack/types`](packages/types)** | Shared runtime type definitions | ๐ŸŸข Active | +| Package | Description | Key Features | Status | +| :--- | :--- | :--- | :--- | +| **[`@objectstack/spec`](packages/spec)** | Protocol definitions (Zod schemas, Types, JSON Schemas) - The "Constitution" | Runtime validation, Type inference, JSON Schema generation | ๐ŸŸข Active | +| **[`@objectstack/core`](packages/core)** | Microkernel runtime (Plugin system, DI, Event Bus, Logger) | Plugin lifecycle, Service registry, High-performance logging | ๐ŸŸข Active | +| **[`@objectstack/types`](packages/types)** | Shared runtime type definitions | IKernel, RuntimePlugin, RuntimeContext interfaces | ๐ŸŸข Active | ### Engine Packages -| Package | Description | Status | -| :--- | :--- | :--- | -| **[`@objectstack/objectql`](packages/objectql)** | ObjectQL query engine and schema registry | ๐ŸŸข Active | -| **[`@objectstack/runtime`](packages/runtime)** | Runtime utilities and plugin helpers | ๐ŸŸข Active | +| Package | Description | Key Features | Status | +| :--- | :--- | :--- | :--- | +| **[`@objectstack/objectql`](packages/objectql)** | ObjectQL query engine and schema registry | Cross-datasource queries, Driver routing, Schema registry | ๐ŸŸข Active | +| **[`@objectstack/runtime`](packages/runtime)** | Runtime utilities and plugin helpers | Standard library, DriverPlugin, AppPlugin | ๐ŸŸข Active | +| **[`@objectstack/metadata`](packages/metadata)** | Metadata loading and persistence | Multi-format support, File watching, Validation | ๐ŸŸข Active | ### Client Packages -| Package | Description | Status | -| :--- | :--- | :--- | -| **[`@objectstack/client`](packages/client)** | Official Client SDK for ObjectStack Protocol | ๐ŸŸข Active | -| **[`@objectstack/client-react`](packages/client-react)** | React hooks for ObjectStack | ๐ŸŸข Active | +| Package | Description | Key Features | Status | +| :--- | :--- | :--- | :--- | +| **[`@objectstack/client`](packages/client)** | Official Client SDK for ObjectStack Protocol | CRUD operations, Batch API, View storage, Error handling | ๐ŸŸข Active | +| **[`@objectstack/client-react`](packages/client-react)** | React hooks for ObjectStack | useQuery, useMutation, usePagination, useInfiniteQuery | ๐ŸŸข Active | ### Plugin Packages -| Package | Description | Status | -| :--- | :--- | :--- | -| **[`@objectstack/driver-memory`](packages/plugins/driver-memory)** | In-memory driver (reference implementation) | ๐ŸŸข Active | -| **[`@objectstack/plugin-hono-server`](packages/plugins/plugin-hono-server)** | HTTP server plugin (Hono-based) | ๐ŸŸข Active | -| **[`@objectstack/plugin-msw`](packages/plugins/plugin-msw)** | Mock Service Worker plugin for testing | ๐ŸŸข Active | +| Package | Description | Key Features | Status | +| :--- | :--- | :--- | :--- | +| **[`@objectstack/driver-memory`](packages/plugins/driver-memory)** | In-memory driver (reference implementation) | Zero dependencies, Perfect for testing, Fast in-memory storage | ๐ŸŸข Active | +| **[`@objectstack/plugin-hono-server`](packages/plugins/plugin-hono-server)** | HTTP server plugin (Hono-based) | Universal runtime, Auto-discovery, API Registry integration | ๐ŸŸข Active | +| **[`@objectstack/plugin-msw`](packages/plugins/plugin-msw)** | Mock Service Worker plugin for testing | Automatic API mocking, Browser & Node support | ๐ŸŸข Active | ### Tools -| Package | Description | Status | -| :--- | :--- | :--- | -| **[`@objectstack/cli`](packages/cli)** | Command-line interface and development tools | ๐ŸŸข Active | -| **[`@objectstack/docs`](apps/docs)** | Documentation site (Fumadocs + Next.js) | ๐ŸŸข Active | +| Package | Description | Key Features | Status | +| :--- | :--- | :--- | :--- | +| **[`@objectstack/cli`](packages/cli)** | Command-line interface and development tools | serve, dev, compile, doctor, create, test:run | ๐ŸŸข Active | +| **[`@objectstack/docs`](apps/docs)** | Documentation site (Fumadocs + Next.js) | Interactive docs, API reference, Examples | ๐ŸŸข Active | + +### Package Quick Links + +**For Application Developers:** +- Start here: [`@objectstack/client`](packages/client/README.md) - Official SDK +- React apps: [`@objectstack/client-react`](packages/client-react/README.md) - React hooks +- Development: [`@objectstack/cli`](packages/cli/README.md) - CLI tools + +**For Protocol Developers:** +- Protocols: [`@objectstack/spec`](packages/spec/README.md) - Schema definitions +- Runtime: [`@objectstack/core`](packages/core/README.md) - Microkernel +- Data layer: [`@objectstack/objectql`](packages/objectql/README.md) - Query engine + +**For Plugin Developers:** +- Runtime: [`@objectstack/runtime`](packages/runtime/README.md) - Plugin patterns +- Types: [`@objectstack/types`](packages/types/README.md) - Shared interfaces +- Examples: + - [`@objectstack/driver-memory`](packages/plugins/driver-memory/README.md) - Driver reference + - [`@objectstack/plugin-hono-server`](packages/plugins/plugin-hono-server/README.md) - Server plugin ### Examples From cf245d09648bd4019f7ce08aacb47988d099e572 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:44:22 +0000 Subject: [PATCH 6/6] Fix code review issues in README documentation Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/cli/README.md | 4 ++-- packages/metadata/README.md | 20 ++++++++++++++++---- packages/objectql/README.md | 14 +++++++++----- packages/runtime/README.md | 11 +++++++---- packages/spec/README.md | 25 +++++++++++++++++++++---- 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 9a19beacb..e4f97bd21 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -122,8 +122,8 @@ objectstack dev @objectstack/core ``` **Behavior:** -- **Single Package Mode**: If `objectstack.config.ts` exists in current directory, delegates to `objectstack serve --dev` -- **Monorepo Mode**: If in workspace root, runs `pnpm dev` with optional package filter +- **Single Package Mode**: If `objectstack.config.ts` exists in current directory, delegates to `objectstack serve --dev` to start a development server with hot reload +- **Monorepo Mode**: If `pnpm-workspace.yaml` exists in current directory (workspace root), executes `pnpm dev` with optional package filter to run development builds across multiple packages in the workspace. The command uses pnpm's workspace filtering to target specific packages or all packages. ### `objectstack compile` diff --git a/packages/metadata/README.md b/packages/metadata/README.md index b5b6db4aa..409d3bf3d 100644 --- a/packages/metadata/README.md +++ b/packages/metadata/README.md @@ -349,6 +349,14 @@ async function versionMetadata() { const objects = await manager.loadMany('object'); + // Get git user name safely + let modifiedBy = 'unknown'; + try { + modifiedBy = execSync('git config user.name', { encoding: 'utf-8' }).trim(); + } catch (error) { + console.warn('Could not get git user name, using "unknown"'); + } + // Add version metadata const versioned = objects.map(obj => ({ ...obj, @@ -356,7 +364,7 @@ async function versionMetadata() { ...obj.metadata, version: '2.0.0', lastModified: new Date().toISOString(), - modifiedBy: execSync('git config user.name').toString().trim() + modifiedBy } })); @@ -368,9 +376,13 @@ async function versionMetadata() { }); } - // Commit to version control - execSync('git add metadata/'); - execSync('git commit -m "Version bump to 2.0.0"'); + // Commit to version control (if git is available) + try { + execSync('git add metadata/', { stdio: 'inherit' }); + execSync('git commit -m "Version bump to 2.0.0"', { stdio: 'inherit' }); + } catch (error) { + console.warn('Git commit failed, changes are staged but not committed'); + } } ``` diff --git a/packages/objectql/README.md b/packages/objectql/README.md index f44831d8f..dc687a871 100644 --- a/packages/objectql/README.md +++ b/packages/objectql/README.md @@ -285,11 +285,15 @@ ql.registerHook('beforeInsert', async (ctx) => { ql.registerHook('afterInsert', async (ctx) => { console.log(`Created ${ctx.object}:`, ctx.result); - // Trigger events - await eventBus.emit('data:created', { - object: ctx.object, - id: ctx.result.id - }); + // Trigger events - get event bus from kernel or context + // Note: eventBus should be injected or accessed from context + const eventBus = ctx.getService?.('event-bus'); + if (eventBus) { + await eventBus.emit('data:created', { + object: ctx.object, + id: ctx.result.id + }); + } }); ql.registerHook('beforeUpdate', async (ctx) => { diff --git a/packages/runtime/README.md b/packages/runtime/README.md index 607707f7b..9d2cf077b 100644 --- a/packages/runtime/README.md +++ b/packages/runtime/README.md @@ -411,9 +411,10 @@ import { Plugin, PluginContext } from '@objectstack/core'; export class ConnectionPoolPlugin implements Plugin { name = 'connection-pool'; + private pool: any; async init(ctx: PluginContext) { - const pool = { + this.pool = { connections: new Map(), getConnection(name: string) { @@ -431,12 +432,14 @@ export class ConnectionPoolPlugin implements Plugin { } }; - ctx.registerService('connection-pool', pool); + ctx.registerService('connection-pool', this.pool); } async destroy() { - const pool = kernel.getService('connection-pool'); - pool.closeAll(); + // Use stored reference from init phase + if (this.pool) { + this.pool.closeAll(); + } } } ``` diff --git a/packages/spec/README.md b/packages/spec/README.md index 0a1690d56..68495df1c 100644 --- a/packages/spec/README.md +++ b/packages/spec/README.md @@ -97,14 +97,31 @@ const app: UI.App = { ### ๐Ÿค– AI-Ready Context -This package includes a `prompts/` directory containing system instructions and architectural context. This is useful for: +This package includes a `prompts/` directory in the package root containing system instructions and architectural context: + +``` +node_modules/@objectstack/spec/prompts/ +โ”œโ”€โ”€ architecture.md # System architecture overview +โ”œโ”€โ”€ plugin/ # Plugin development guides +โ””โ”€โ”€ development/ # Development workflows +``` + +These prompts are useful for: 1. **AI Agents**: Creating agents that understand ObjectStack. 2. **IDE Context**: Adding `node_modules/@objectstack/spec/prompts/*.md` to your Cursor/Copilot context. -3. **LLM Auto-Discovery**: The `llms.txt` file in the root provides a knowledge base for autonomous agents. +3. **LLM Auto-Discovery**: The `llms.txt` file in the repository root provides a knowledge base for autonomous agents. ```typescript -import context from '@objectstack/spec/prompts/architecture.md?raw'; // If using Vite/bundler -// Or just read the file from disk +// If using Vite/bundler with raw import support +import context from '@objectstack/spec/prompts/architecture.md?raw'; + +// Or read from disk in Node.js +import fs from 'fs'; +import path from 'path'; +const context = fs.readFileSync( + path.join(process.cwd(), 'node_modules/@objectstack/spec/prompts/architecture.md'), + 'utf-8' +); ``` #### 1. Namespace Imports from Root