Thank you for your interest in contributing to ObjectOS! This guide will help you get started.
ObjectOS is the runtime engine that executes metadata defined in the ObjectQL format. It's part of a two-repository ecosystem:
- objectql/objectql: The protocol definition and core drivers
- objectql/objectos (this repo): The runtime implementation
This is a Monorepo managed by PNPM workspaces and organized as follows:
objectos/
├── packages/
│ ├── kernel/ # @objectos/kernel - Core runtime engine
│ ├── server/ # @objectos/server - NestJS HTTP server
│ └── presets/ # @objectos/preset-* - Standard metadata
├── apps/
│ └── site/ # @objectos/site - Documentation site
├── examples/ # Example applications
└── docs/ # VitePress documentation
Note: The UI package (@objectos/ui) has been moved to a separate repository and is developed independently.
| Package | Role | Can Import | Cannot Import |
|---|---|---|---|
@objectos/kernel |
Core logic, object registry, hooks | @objectql/types, @objectql/core |
pg, express, nest |
@objectos/server |
HTTP layer, REST API | @objectos/kernel, @nestjs/* |
knex, direct SQL |
@objectos/preset-* |
Metadata YAML files | None | No .ts files allowed |
"Kernel handles logic, Drivers handle data, Server handles HTTP."
This must be maintained at all times:
- Kernel never touches HTTP or database connections directly
- Server never touches database queries directly
- Drivers are injected via dependency injection
TypeScript
- Use strict mode (
strict: truein tsconfig) - No
any- useunknownwith type guards if needed - Prefer interfaces over type aliases for public APIs
- Use async/await for all I/O operations
Naming Conventions
- Files:
kebab-case.ts - Classes:
PascalCase - Functions/variables:
camelCase - Interfaces:
PascalCase(noIprefix) - Constants:
UPPER_SNAKE_CASE
Comments
- Use JSDoc for all public APIs
- Explain why, not just what
- Include examples for complex functions
Example:
/**
* Loads an object definition into the registry.
* Triggers a schema sync if the driver supports it.
*
* @param config The object metadata from YAML
* @throws {ValidationError} If the config is invalid
*
* @example
* await kernel.load({
* name: 'contacts',
* fields: { email: { type: 'email' } }
* });
*/
async load(config: ObjectConfig): Promise<void> {
// ...
}// ❌ BAD
interface ObjectConfig {
name: string;
fields: any;
}
// ✅ GOOD
import { ObjectConfig } from '@objectql/types';// ❌ BAD
async find(name: string, opts: any): Promise<any> {
// ...
}
// ✅ GOOD
import { FindOptions } from '@objectql/types';
async find(
name: string,
options: FindOptions
): Promise<Record<string, any>[]> {
// ...
}All new features must include tests.
- Unit Tests: For kernel logic (target: 90%+ coverage)
- Integration Tests: For server endpoints (target: 80%+ coverage)
- E2E Tests: For critical user flows
Example test:
describe('ObjectOS.insert', () => {
let kernel: ObjectOS;
let mockDriver: jest.Mocked<ObjectQLDriver>;
beforeEach(() => {
kernel = new ObjectOS();
mockDriver = createMockDriver();
kernel.useDriver(mockDriver);
});
it('should validate required fields', async () => {
await kernel.load({
name: 'contacts',
fields: {
email: { type: 'email', required: true }
}
});
await expect(
kernel.insert('contacts', {}) // Missing email
).rejects.toThrow('email is required');
});
});- Update relevant docs in
/docsfor any user-facing changes - Add JSDoc comments for all public APIs
- Include migration notes for breaking changes
- Update CHANGELOG.md
- Node.js 18+ (LTS recommended)
- PNPM 8+
- PostgreSQL 13+ (for integration tests)
# Clone the repository
git clone https://github.com/objectql/objectos.git
cd objectos
# Install dependencies
pnpm install
# Build all packages
pnpm run build
# Run tests
pnpm run test# Start the full stack development environment
# - Server runs in watch mode on http://localhost:3000
# - Web runs in build watch mode (changes auto-compile)
pnpm run dev
# Start only the server (without frontend build watch)
pnpm run server
# Start only the frontend build watch
pnpm run web:watch
# Build for production (compiles both server and web)
pnpm run build
# Run the production build (starts server serving built web assets)
pnpm run start
# Run tests in watch mode
pnpm run test --watch
# Build documentation
pnpm run docs:dev
# Lint code
pnpm run lint # (TODO: Add lint script)- Check GitHub Issues
- Look for labels:
good first issue,help wanted - Comment on the issue to claim it
# Create a feature branch
git checkout -b feature/your-feature-name
# Or a bugfix branch
git checkout -b fix/issue-123Follow the coding standards above and ensure:
- Code compiles without errors
- Tests pass
- Documentation is updated
- No regressions
# Run tests for specific package
cd packages/kernel
pnpm run test
# Run all tests
cd ../..
pnpm run testUse conventional commits:
# Format: <type>(<scope>): <subject>
git commit -m "feat(kernel): add hook priority support"
git commit -m "fix(server): handle null values in query"
git commit -m "docs(guide): add architecture examples"Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Code style changes (formatting)refactor: Code refactoringtest: Adding or updating testschore: Build process or tooling changes
# Push your branch
git push origin feature/your-feature-name
# Create a Pull Request on GitHub
# - Provide a clear description
# - Reference related issues
# - Add screenshots for UI changesBefore submitting, ensure:
- Code follows style guidelines
- All tests pass
- New tests added for new features
- Documentation updated
- No merge conflicts
- Conventional commit messages used
# All tests
pnpm run test
# Specific package
pnpm --filter @objectos/kernel test
# With coverage
pnpm run test --coverage
# Watch mode
pnpm run test --watch// packages/kernel/src/__tests__/registry.test.ts
import { ObjectRegistry } from '../registry';
describe('ObjectRegistry', () => {
let registry: ObjectRegistry;
beforeEach(() => {
registry = new ObjectRegistry();
});
it('should register an object', () => {
registry.register({
name: 'contacts',
fields: { email: { type: 'email' } }
});
expect(registry.has('contacts')).toBe(true);
});
it('should throw if object not found', () => {
expect(() => {
registry.get('nonexistent');
}).toThrow('Object not found: nonexistent');
});
});// packages/server/test/api.e2e-spec.ts
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { AppModule } from '../src/app.module';
describe('ObjectDataController (e2e)', () => {
let app;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('POST /api/data/contacts should create contact', () => {
return request(app.getHttpServer())
.post('/api/data/contacts')
.send({
first_name: 'John',
last_name: 'Doe',
email: 'john@example.com'
})
.expect(201)
.expect((res) => {
expect(res.body).toHaveProperty('id');
});
});
afterAll(async () => {
await app.close();
});
});# Start docs dev server
pnpm run docs:dev
# Build static docs
pnpm run docs:build
# Preview built docs
pnpm run docs:previewdocs/
├── index.md # Homepage
├── guide/
│ ├── index.md # Getting Started
│ ├── architecture.md # Architecture guide
│ ├── data-modeling.md # Data modeling
│ ├── logic-hooks.md # Writing hooks
│ └── ...
└── spec/
├── index.md # Spec overview
├── metadata-format.md
└── ...
- Use clear, concise language
- Include code examples
- Add diagrams for complex concepts
- Link to related documentation
- Test all code examples
Releases are managed by maintainers using Changesets.
# Create a changeset
pnpm changeset
# Version packages
pnpm version
# Publish to NPM
pnpm release- Discord: Coming soon
- GitHub Issues: For bugs and feature requests
- GitHub Discussions: For questions and discussions
- Be respectful and inclusive
- Provide constructive feedback
- Focus on the code, not the person
- Help newcomers get started
By contributing, you agree that your contributions will be licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Thank you for contributing to ObjectOS! 🎉