B
Blocks
Specification

Store API Reference

The @blocksai/store package provides a TypeScript API for reading and writing Blocks configuration to SQLite or PostgreSQL databases. Use it to build tools, dashboards, or automated workflows.

Installation

pnpm add @blocksai/store

For PostgreSQL support, also install:

pnpm add pg

Quick Example

import { BlocksStore } from '@blocksai/store';

const store = new BlocksStore('sqlite:///blocks.db');

// Initialize database schema
await store.initialize();

// Store a block
await store.putBlock('user_validator', {
  description: 'Validates user data',
  inputs: [{ name: 'user', type: 'entity.user' }],
  outputs: [{ name: 'valid', type: 'boolean' }],
});

// Retrieve it
const block = await store.getBlock('user_validator');
console.log(block.description); // "Validates user data"

// Close connection
await store.close();

BlocksStore Class

Constructor

new BlocksStore(url: string)

Create a new store instance:

// SQLite
const store = new BlocksStore('sqlite:///blocks.db');
const memoryStore = new BlocksStore('sqlite:///:memory:');

// PostgreSQL
const pgStore = new BlocksStore('postgres://user:pass@localhost/blocks');

Parameters:

  • url - Database connection string
    • SQLite: sqlite:///path/to/file.db
    • PostgreSQL: postgres://user:pass@host:port/database

Throws:

  • Error if URL format is invalid

initialize()

async initialize(): Promise<void>

Create database tables if they don't exist. Call this before first use:

await store.initialize();

Tables created:

  • blocks - Block definitions
  • entities - Entity definitions
  • semantics - Semantic definitions
  • config - Key-value configuration (philosophy, project, etc.)

Safe to call multiple times - uses CREATE TABLE IF NOT EXISTS.

Block Operations

getBlocks()

async getBlocks(): Promise<Record<string, BlockDefinition>>

Retrieve all blocks:

const blocks = await store.getBlocks();
// {
//   user_validator: { description: '...', inputs: [...], ... },
//   email_sender: { description: '...', ... }
// }

Returns: Object mapping block names to definitions.

getBlock()

async getBlock(name: string): Promise<BlockDefinition | null>

Retrieve a single block by name:

const block = await store.getBlock('user_validator');
if (block) {
  console.log(block.description);
}

Parameters:

  • name - Block name (e.g., "user_validator")

Returns: Block definition or null if not found.

putBlock()

async putBlock(name: string, block: BlockDefinition): Promise<void>

Store or update a block:

await store.putBlock('user_validator', {
  description: 'Validates user input',
  inputs: [
    { name: 'user', type: 'entity.user' }
  ],
  outputs: [
    { name: 'valid', type: 'boolean' },
    { name: 'errors', type: 'array', optional: true }
  ],
  domain_rules: [
    { id: 'email_check', description: 'Must validate email format' }
  ],
});

Parameters:

  • name - Block name
  • block - Block definition object

Behavior: Creates if doesn't exist, updates if it does (upsert).

deleteBlock()

async deleteBlock(name: string): Promise<void>

Remove a block:

await store.deleteBlock('old_block');

Parameters:

  • name - Block name to delete

Behavior: Silent if block doesn't exist.

Entity Operations

getEntities()

async getEntities(): Promise<Record<string, EntityDefinition>>

Retrieve all entities:

const entities = await store.getEntities();
// {
//   user: { fields: ['id', 'name', 'email'] },
//   product: { fields: ['id', 'title', 'price'] }
// }

putEntity()

async putEntity(name: string, entity: EntityDefinition): Promise<void>

Store or update an entity:

await store.putEntity('user', {
  fields: ['id', 'name', 'email', 'created_at'],
  optional: ['phone'],
});

Semantic Operations

getSemantics()

async getSemantics(): Promise<Record<string, SemanticDefinition>>

Retrieve all semantic definitions:

const semantics = await store.getSemantics();
// {
//   money_cents: {
//     description: 'Monetary value in cents',
//     schema: { type: 'integer', minimum: 0 }
//   }
// }

putSemantic()

async putSemantic(name: string, semantic: SemanticDefinition): Promise<void>

Store or update a semantic:

await store.putSemantic('money_cents', {
  description: 'Monetary value in cents (integer)',
  schema: {
    type: 'integer',
    minimum: 0,
  },
});

Configuration Operations

getConfig()

async getConfig(key: string): Promise<any>

Retrieve a configuration value by key:

const philosophy = await store.getConfig('philosophy');
// ['Blocks must be composable', 'Validate everything']

const project = await store.getConfig('project');
// { name: 'my-project', domain: 'myproject.general' }

Common keys:

  • philosophy - Philosophy statements (string array)
  • project - Project metadata (object)
  • validators - Validator configuration (array)
  • pipeline - Pipeline configuration (object)
  • agent - Agent configuration (object)
  • targets - Targets configuration (object)
  • ai - AI provider configuration (object)

Returns: null if key doesn't exist.

putConfig()

async putConfig(key: string, value: any): Promise<void>

Store or update a configuration value:

await store.putConfig('philosophy', [
  'Blocks must be small and composable',
  'All blocks must validate through multi-layer checks',
]);

await store.putConfig('project', {
  name: 'my-project',
  domain: 'myproject.general',
});

Parameters:

  • key - Configuration key
  • value - Any JSON-serializable value

Conversion Methods

toBlocksConfig()

async toBlocksConfig(): Promise<BlocksConfig>

Convert entire database to a BlocksConfig object:

const config = await store.toBlocksConfig();
// {
//   project: { name: '...', domain: '...' },
//   philosophy: [...],
//   domain: {
//     entities: {...},
//     semantics: {...}
//   },
//   blocks: {...},
//   ...
// }

Returns: Complete configuration object matching blocks.yml schema.

Use case: Export database to YAML file.

fromBlocksConfig()

async fromBlocksConfig(config: BlocksConfig): Promise<void>

Import a complete BlocksConfig into the database:

import { readFileSync } from 'fs';
import { parse } from 'yaml';

const yaml = readFileSync('blocks.yml', 'utf-8');
const config = parse(yaml);

await store.fromBlocksConfig(config);

Parameters:

  • config - Complete BlocksConfig object

Behavior: Overwrites existing data with values from config. Missing fields are not deleted.

Use case: Import from YAML file or another source.

Utility Functions

resolveConfig()

import { resolveConfig } from '@blocksai/store';

const resolved = await resolveConfig(localConfig, sources);

Merge a local config with sources according to the merge strategy:

const localConfig = {
  project: { name: 'my-project' },
  blocks: {
    my_block: { description: 'Local block' }
  }
};

const sources = [
  { type: 'database', url: 'sqlite:///shared.db', mode: 'pull' },
  { type: 'file', path: './common.yml' }
];

const resolved = await resolveConfig(localConfig, sources);
// Merged result with local overrides

Parameters:

  • localConfig - Base configuration object
  • sources - Array of source definitions

Returns: Merged configuration object.

Use case: Implement custom config loading logic.

Connection Management

close()

async close(): Promise<void>

Close the database connection:

await store.close();

Best practice: Always close connections when done, especially in long-running applications or scripts:

const store = new BlocksStore('sqlite:///blocks.db');
try {
  await store.initialize();
  // ... do work ...
} finally {
  await store.close();
}

TypeScript Types

BlockDefinition

interface BlockDefinition {
  description: string;
  path?: string;
  inputs?: Array<{
    name: string;
    type: string;
    optional?: boolean;
  }>;
  outputs?: Array<{
    name: string;
    type: string;
    measures?: string[];
    constraints?: string[];
    optional?: boolean;
  }>;
  domain_rules?: Array<{
    id: string;
    description: string;
  }>;
  test_data?: any;
}

EntityDefinition

interface EntityDefinition {
  fields: string[];
  optional?: string[];
}

SemanticDefinition

interface SemanticDefinition {
  description: string;
  schema?: any; // JSON Schema object
}

BlocksConfig

interface BlocksConfig {
  project?: {
    name: string;
    domain: string;
  };
  philosophy?: string[];
  domain?: {
    entities?: Record<string, EntityDefinition>;
    semantics?: Record<string, SemanticDefinition>;
    signals?: Record<string, any>;
    measures?: Record<string, any>;
  };
  blocks?: Record<string, BlockDefinition>;
  validators?: any[];
  pipeline?: any;
  agent?: any;
  targets?: any;
  ai?: any;
  cache?: any;
  sources?: Array<{
    type: 'database' | 'file';
    url?: string;
    path?: string;
    mode?: 'pull' | 'push' | 'sync';
  }>;
}

Complete Example

Here's a complete example showing common operations:

import { BlocksStore } from '@blocksai/store';
import { writeFileSync } from 'fs';
import { stringify } from 'yaml';

async function main() {
  const store = new BlocksStore('postgres://localhost/blocks');

  try {
    // Initialize database
    await store.initialize();

    // Store project metadata
    await store.putConfig('project', {
      name: 'user-service',
      domain: 'users.api',
    });

    // Store philosophy
    await store.putConfig('philosophy', [
      'All user data must be validated',
      'Privacy is paramount',
    ]);

    // Store an entity
    await store.putEntity('user', {
      fields: ['id', 'email', 'name'],
      optional: ['phone', 'avatar'],
    });

    // Store a block
    await store.putBlock('user_validator', {
      description: 'Validates user input',
      inputs: [
        { name: 'user', type: 'entity.user' }
      ],
      outputs: [
        { name: 'valid', type: 'boolean' },
        { name: 'errors', type: 'array', optional: true }
      ],
    });

    // Retrieve and display a block
    const block = await store.getBlock('user_validator');
    console.log('Block:', block);

    // Export entire config to YAML
    const config = await store.toBlocksConfig();
    const yaml = stringify(config);
    writeFileSync('exported-blocks.yml', yaml);
    console.log('Exported to exported-blocks.yml');

    // Get all blocks
    const allBlocks = await store.getBlocks();
    console.log('Total blocks:', Object.keys(allBlocks).length);

  } finally {
    await store.close();
  }
}

main().catch(console.error);

Error Handling

All async methods can throw errors. Common error cases:

import { BlocksStore } from '@blocksai/store';

const store = new BlocksStore('postgres://invalid-host/blocks');

try {
  await store.initialize();
} catch (error) {
  if (error.code === 'ECONNREFUSED') {
    console.error('Database server is not running');
  } else if (error.code === 'ENOTFOUND') {
    console.error('Database host not found');
  } else {
    console.error('Database error:', error.message);
  }
}

Common error scenarios:

  • Connection refused (database not running)
  • Authentication failure (wrong credentials)
  • Network timeout (unreachable host)
  • Permission denied (insufficient privileges)
  • Invalid URL format (malformed connection string)

Next Steps