# Agents Guide

This file follows the [agents.md](https://agents.md/) specification for AI agent
context. If you're a human reading this, it provides a comprehensive overview of
the TinyBase project for AI assistants working with the codebase.

## Overview

**TinyBase** is a reactive data store and sync engine for local-first
applications. It is a TypeScript library that provides a reactive, in-memory
data store with a powerful synchronization engine. It's designed for building
local-first applications that work offline and sync across devices. The library
is exceptionally small (5.3kB-11.7kB), has zero runtime dependencies, and
maintains 100% test coverage.

- **Website**: https://tinybase.org
- **Repository**: https://github.com/tinyplex/tinybase
- **Documentation**: https://tinybase.org/api/
- **License**: MIT
- **Author**: James Pearce (@jamesgpearce)

## Core Concepts

### Data Store

TinyBase provides two types of data structures:

- **Tables**: Tabular data organized as Table → Row → Cell (similar to
  relational databases)
- **Values**: Simple key-value pairs for application state

Both can coexist in the same Store and support optional schemas with type
enforcement.

### Reactivity

The library implements a fine-grained reactive system where you can listen to
changes at any level:

- Entire store changes
- Table/value additions or removals
- Row changes within a table
- Individual cell or value changes

Listeners fire automatically when data changes, enabling efficient UI updates
that only re-render affected components.

### Synchronization

TinyBase includes native CRDT (Conflict-free Replicated Data Type) support via
the MergeableStore, allowing deterministic synchronization across multiple
clients and servers using Hybrid Logical Clocks for causality tracking.

## Key Features

### Data Management

- **Schemas**: Optional TypeScript-inferred schemas for type safety
- **Indexes**: Fast lookups by cell values with slice-based grouping
- **Queries**: SQL-like query engine (select, join, filter, group) without
  actual SQL
- **Relationships**: Define foreign-key relationships between tables
- **Metrics**: Built-in aggregations (sum, avg, min, max)
- **Checkpoints**: Undo/redo functionality with branching support

### Persistence

Multiple storage backends supported via Persisters:

- **Browser**: LocalStorage, SessionStorage, IndexedDB, OPFS
- **Databases**: SQLite (Bun, WASM, sqlite3), PostgreSQL, PGlite, Turso (libSQL)
- **Third-party**: ElectricSQL, PowerSync, CR-SQLite
- **Cloud**: PartyKit, Cloudflare Durable Objects
- **Files**: Node.js file system
- **CRDT**: Yjs, Automerge integration
- **React Native**: MMKV, SQLite

### Synchronization

Synchronizers enable real-time data sync:

- WebSocket (client and server)
- BroadcastChannel (same-origin tabs)
- Local (in-memory for testing)
- Custom transports (extensible)

### React Integration

Optional `ui-react` module provides:

- **Hooks**: `useCell`, `useRow`, `useTable`, `useTables`, `useValue`, etc.
- **Components**: Pre-built reactive views for data rendering
- **Context**: Multi-store support with ID-based contexts
- **DOM Components**: `ui-react-dom` with interactive tables
- **Inspector**: Developer tools overlay for debugging

## Architecture

### Modular Design

TinyBase uses a modular architecture where each feature is an independent module
that can be imported separately:

```
tinybase              # Core store module
tinybase/indexes      # Indexing
tinybase/queries      # Query engine
tinybase/relationships # Relationships
tinybase/metrics      # Aggregations
tinybase/checkpoints  # Undo/redo
tinybase/mergeable-store # CRDT support
tinybase/persisters/persister-* # Storage backends
tinybase/synchronizers/synchronizer-* # Sync transports
tinybase/ui-react     # React hooks
tinybase/ui-react-dom # React DOM components
tinybase/ui-react-inspector # DevTools
```

### Type System

Strong TypeScript support with:

- Generic types that infer from schemas
- Conditional types for schema-aware APIs
- Mapped types for compile-time validation
- Type-safe hooks and components

### Build System

- **Gulp**: Build orchestration
- **TypeScript**: Source language with strict mode
- **Rollup**: Bundling (implied)
- **ESM**: Primary module format
- **Tree-shaking**: Aggressive optimization for minimal bundles

## Development

### Prerequisites

- Node.js >= 23.10.0
- npm >= 10.9.2

### Setup

```bash
git clone https://github.com/tinyplex/tinybase.git
cd tinybase
npm install
```

### Common Commands

```bash
npm run compileAndTestUnit  # Compile and run unit tests
npm run testUnitFast        # Quick test iteration
npm run lint                # Run ESLint
npm run spell               # Spell check
npm run preCommit           # Full pre-commit check
npm run compileDocs         # Generate API documentation
npm run serveDocs           # Preview documentation locally
```

### Testing

- **Framework**: Vitest
- **Coverage**: 100% required (enforced)
- **Types**: Unit, performance, end-to-end, production
- **Environment**: happy-dom (unit), puppeteer (e2e)

### Code Style

- **ESLint**: Enforced with strict rules
- **Prettier**: Automatic formatting
- **Max line length**: 80 characters
- **Quotes**: Single quotes (template literals allowed)
- **Semicolons**: Required
- **Object spacing**: No spaces in braces `{key: value}`

## Project Structure

```
tinybase/
├── src/                    # Source code
│   ├── @types/            # TypeScript declarations
│   ├── store/             # Core store implementation
│   ├── indexes/           # Indexing module
│   ├── queries/           # Query engine
│   ├── relationships/     # Relationships module
│   ├── metrics/           # Metrics module
│   ├── checkpoints/       # Checkpoints module
│   ├── mergeable-store/   # CRDT implementation
│   ├── persisters/        # Storage backends
│   ├── synchronizers/     # Sync transports
│   ├── ui-react/          # React hooks
│   ├── ui-react-dom/      # React DOM components
│   ├── ui-react-inspector/ # DevTools
│   └── common/            # Shared utilities
├── test/                  # Tests
│   ├── unit/             # Unit tests
│   ├── perf/             # Performance tests
│   ├── e2e/              # End-to-end tests
│   └── prod/             # Production build tests
├── docs/                  # Generated documentation
├── dist/                  # Build output
├── site/                  # Documentation site source
├── gulpfile.mjs          # Build configuration
├── vitest.config.ts      # Test configuration
├── eslint.config.js      # Linting rules
└── tsconfig.json         # TypeScript config
```

## Contributing

Contributions are welcome! This is a spare-time project, so response times may
vary.

**Requirements**:

1. Follow the Prettier and ESLint configurations
2. Maintain 100% test coverage
3. Update documentation for API changes
4. Add examples for new features

**Process**:

1. Fork the repository
2. Create a feature branch
3. Make your changes with tests
4. Run `npm run preCommit` to verify
5. Submit a pull request

See [CONTRIBUTING.md](CONTRIBUTING.md) for details.

## Community

- **Discord**: https://discord.com/invite/mGz3mevwP8
- **Discussions**: https://github.com/tinyplex/tinybase/discussions
- **Issues**: https://github.com/tinyplex/tinybase/issues
- **Bluesky**: https://bsky.app/profile/tinybase.bsky.social
- **Twitter/X**: https://x.com/tinybasejs

## Use Cases

TinyBase is ideal for:

- **Local-first applications**: Apps that work offline and sync later
- **Real-time collaboration**: Multi-user applications with CRDT sync
- **Reactive UIs**: Applications requiring fine-grained reactivity
- **Mobile apps**: React Native apps with local storage
- **Edge computing**: Cloudflare Workers, Durable Objects
- **Progressive Web Apps**: Offline-capable web applications
- **Games**: Real-time state management with undo/redo
- **Data dashboards**: Reactive data visualization

## Performance

- Tiny bundle sizes (5.3kB - 11.7kB depending on features)
- Zero runtime dependencies
- Efficient change detection and listener notification
- Memory pooling for ID generation
- Tree-shakeable modular design
- Optimized for bundle size and runtime performance

## Related Projects

- **Synclets**: Generic synchronization library (https://synclets.org)
- **TinyWidgets**: Widget toolkit built on TinyBase (https://tinywidgets.org)
- **TinyTick**: Reactive ticker tape component (https://tinytick.org)

## License

MIT License - see [LICENSE](LICENSE) file for details.

---

**Note for AI Agents**: TinyBase uses unique patterns including utility function
wrappers (e.g., `arrayForEach`, `mapGet`, `objHas`) instead of native methods
for consistency and tree-shaking. Always use factory functions (`createStore`,
`createIndexes`, etc.) with builder pattern chaining. Maintain 100% test
coverage and follow the strict 80-character line length. See
`.github/copilot-instructions.md` for detailed coding patterns.

## Documentation System

TinyBase has a sophisticated documentation system that generates the website
from source code and markdown files.

### Documentation Structure

1. **Type Definitions (`src/@types/*/`)**: TypeScript `.d.ts` files contain the
   API type definitions. **Never add comments directly to `.d.ts` files**.

2. **Documentation Files (`src/@types/*/docs.js`)**: Companion `docs.js` files
   sit alongside `.d.ts` files. Use `///` convention to document types and
   functions. These are stitched together at build time to generate
   documentation.

3. **Guide Files (`site/guides/*/*.md`)**: Markdown files in the `site/guides/`
   directory, organized by topic (basics, schemas, persistence, etc.). These are
   source files for guides on the website.

4. **Generated Files**: `/releases.md` and `/readme.md` in the root are
   **GENERATED** from `/site/guides/16_releases.md` and `/site/home/index.md`.
   **Never edit the generated files directly**.

### Documentation Testing

TinyBase has automated tests that validate all inline code examples in
documentation:

```bash
npx vitest run ./test/unit/documentation.test.ts --retry=0
```

**How it works**:

- Extracts all code blocks from markdown files and `docs.js` files
- Concatenates all examples from each file together
- Parses and executes them to ensure they work
- This means examples in the same file share scope

**Critical constraints**:

- Don't redeclare variables across examples in the same file
- First example can declare `const store = createStore()`, subsequent examples
  reuse it
- Include necessary imports in examples that use them
- Avoid async operations in examples unless necessary
- Keep examples simple and focused

**Common pitfalls**:

- ❌ Declaring `const store` multiple times in the same file
- ❌ Using undefined functions (forgot import statement)
- ✅ First example: `const store = createStore()`
- ✅ Later examples: `store.setCell(...)` (reuses existing store)

### Adding New Documentation

1. **API Documentation**: Edit `docs.js` file next to the type definition
2. **Guide Content**: Edit markdown files in `/site/guides/`
3. **Release Notes**: Edit `/site/guides/16_releases.md` (not `/releases.md`)
4. **Always run documentation tests** after changes to verify examples work

## Creating New Schematizers

Schematizers convert external schema validation libraries (like Zod) to TinyBase
schemas. Follow this pattern:

### Module Structure

```
src/@types/schematizers/schematizer-{library}/
  index.d.ts           # Type definitions
  docs.js              # Documentation
  with-schemas/
    index.d.ts         # Re-exports for schema-aware variants
src/schematizers/schematizer-{library}/
  index.ts             # Implementation
```

### Factory Pattern

```typescript
export const createZodSchematizer: typeof createZodSchematizerDecl = () => {
  const toTablesSchema = (schemas: {[tableId: string]: any}): TablesSchema => {
    // Best-effort conversion logic
  };

  const toValuesSchema = (schemas: {[valueId: string]: any}): ValuesSchema => {
    // Best-effort conversion logic
  };

  return objFreeze({
    toTablesSchema,
    toValuesSchema,
  });
};
```

### Conversion Strategy

- Extract basic types only: `string`, `number`, `boolean`
- Handle defaults via schema introspection
- Support nullable and optional modifiers
- **Ignore** complex types (arrays, objects, etc.) - they won't appear in output
- Use recursive unwrapping for wrapper types (e.g., `ZodOptional`, `ZodNullable`,
  `ZodDefault`)

### Implementation Idioms

- Use `objForEach` for iteration, not `for...in` loops
- Use `ifNotUndefined` for conditional logic
- Use `objIsEmpty` to filter out empty table schemas
- Extract string constants to module-level (e.g., `TYPE`, `DEFAULT`,
  `ALLOW_NULL`)
- Freeze the returned schematizer object with `objFreeze`

### Example Conversion Logic

```typescript
const unwrap = (
  schema: any,
  defaultValue?: any,
  allowNull?: boolean,
): [any, any, boolean] => {
  const typeName = schema._def?.typeName;
  return typeName === ZOD_OPTIONAL
    ? unwrap(schema._def.innerType, defaultValue, allowNull)
    : typeName === ZOD_NULLABLE
      ? unwrap(schema._def.innerType, defaultValue, true)
      : typeName === ZOD_DEFAULT
        ? unwrap(schema._def.innerType, schema._def.defaultValue(), allowNull)
        : [schema, defaultValue, allowNull ?? false];
};
```

### Build Configuration

- Add module to `ALL_MODULES` array in `gulpfile.mjs`
- Add peer dependency to `package.json` (marked as optional)
- Add as dev dependency for testing

### Testing

- Create comprehensive test suite in
  `test/unit/schematizers/schematizer-{library}.test.ts`
- Test basic type conversion, defaults, nullable, optional
- Test unsupported types are filtered out
- Test integration with actual TinyBase stores
- Inline schemas directly in test calls (no intermediate variables unless needed
  multiple times)

### Documentation Testing

Add library import to `test/unit/documentation.test.ts`:

```typescript
import * as z from 'zod';
import * as TinyBaseSchematizersZod from 'tinybase/schematizers/schematizer-zod';

(globalThis as any).modules = {
  ...
  'tinybase/schematizers/schematizer-zod': TinyBaseSchematizersZod,
  zod: z,
};
```

## Guide Writing Best Practices

### Examples Run Sequentially

All code examples in a guide file are concatenated and executed as a test:

- Use unique variable names (`store`, `store2`, `store3`) to avoid redeclaration
- First example includes all imports, later examples reuse them
- Clean up between examples if needed (`store.delTables()`)

### Inline Simple Values

- Prefer inline schemas/data in method calls over intermediate variables
- Only extract to variables when used multiple times
- Keeps examples concise and focused

### Guide Chains

- Each guide's summary should link to the next guide in sequence
- Pattern: "For that we proceed to the [Next Topic] guide."
- Creates a natural learning path

## Release Notes Updates

When adding a new feature:

1. **Update `/site/guides/16_releases.md`** (NOT `/releases.md`):
   - Add new version section at the top
   - Include working code example that will be tested
   - Link to relevant guide if applicable
   - Use past releases as template for structure

2. **Update `/site/home/index.md`**:
   - Update the "NEW!" link to point to new version:
     `<a href='/guides/releases/#v7-1'>`
   - Update the tagline:
     `<span id="one-with">"The one with Schematizers!"</span>`

3. **Generated files update automatically** during build process

## Demo Development Workflow

Demos are located in `/site/demos/` as markdown files containing embedded code
blocks that are assembled into working applications.

### Demo Structure

- **Demo files**: `/site/demos/*.md` or `/site/demos/*/`
- **E2E tests**: `/test/e2e/demos/*.test.ts`
- Code blocks in markdown are extracted and combined into complete applications
- All code fragments in a demo share scope (variables declared in one block are
  available in subsequent blocks)

### Iteration Workflow

When modifying demos:

```bash
# One-time setup (only if TinyBase source code has changed)
npm run compileForProd

# Fast iteration loop
npm run compileDocsPagesOnly  # Rebuild just the demo pages
npm run testE2e               # Run E2E tests to verify demos
```

**Key points**:

- `compileForProd` builds the TinyBase libraries themselves
- `compileDocsPagesOnly` is much faster - only rebuilds demo pages from markdown
- You only need `compileForProd` once, unless you've changed TinyBase source
- E2E tests use Playwright to verify demos work in a real browser
- Individual E2E tests can be run for faster verification during iteration
