A state machine for managing DuckDB operations in web applications. This library provides a type-safe interface for database initialization, query execution, transaction management, and table catalog operations.
- State Management: Full XState integration for predictable database state management
- DuckDB Integration: Built on top of
@duckdb/duckdb-wasmfor browser-based analytics - Transaction Support: Complete transaction lifecycle management (begin, execute, commit, rollback)
- Table Catalog: Dynamic table loading, versioning, and management
- Type Safety: Full TypeScript support with comprehensive type definitions
- Multiple Data Formats: Support for Arrow IPC and JSON data formats
- Compression: Built-in support for data compression (zlib)
- Real-time Updates: Subscription-based table change notifications
npm install @jr200/xstate-duckdb
# or
yarn add @jr200/xstate-duckdb
# or
pnpm add @jr200/xstate-duckdbThe duckdbMachine has the following states:
idle: Initial state, waiting for configurationconfigured: Database configured, ready to connectinitializing: Database initialization in progressconnected: Database connected and ready for operationsdisconnected: Database disconnectederror: Error state
CONFIGURE: Configure database parameters and catalogRESET: Reset to initial state
CONNECT: Initialize and connect to databaseDISCONNECT: Disconnect from database
QUERY.EXECUTE: Execute a one-shot query with auto-commit
TRANSACTION.BEGIN: Start a new transactionTRANSACTION.EXECUTE: Execute a query within a transactionTRANSACTION.COMMIT: Commit the current transactionTRANSACTION.ROLLBACK: Rollback the current transaction
CATALOG.SUBSCRIBE: Subscribe to table changes with a subscription objectCATALOG.UNSUBSCRIBE: Unsubscribe from table changes using subscription IDCATALOG.LIST_TABLES: List all loaded tablesCATALOG.LOAD_TABLE: Load data into a tableCATALOG.DROP_TABLE: Drop a tableCATALOG.LIST_DEFINITIONS: Get catalog configuration
import { duckdbMachine } from '@jr200/xstate-duckdb'
import { useActor } from '@xstate/react'
function DatabaseComponent() {
const [state, send] = useActor(duckdbMachine)
const initializeDB = () => {
send({
type: 'CONFIGURE',
dbInitParams: {
logLevel: LogLevel.INFO,
config: {},
},
catalogConfig: {},
})
send({ type: 'CONNECT' })
}
const runQuery = () => {
send({
type: 'QUERY.EXECUTE',
queryParams: {
sql: 'SELECT 1 as test',
callback: (result) => console.log(result),
description: 'test_query',
resultType: 'json',
},
})
}
return (
<div>
<button onClick={initializeDB}>Initialize DB</button>
<button onClick={runQuery}>Run Query</button>
</div>
)
}const handleTransaction = () => {
// Begin transaction
send({ type: 'TRANSACTION.BEGIN' })
// Execute queries within transaction
send({
type: 'TRANSACTION.EXECUTE',
queryParams: {
sql: 'INSERT INTO users (name) VALUES ("John")',
callback: (result) => console.log('Insert result:', result),
description: 'insert_user',
resultType: 'json',
},
})
// Commit or rollback
send({ type: 'TRANSACTION.COMMIT' })
// or send({ type: 'TRANSACTION.ROLLBACK' })
}const handleTableOperations = () => {
// Load a table with Arrow data
send({
type: 'CATALOG.LOAD_TABLE',
tableName: 'my_table',
tablePayload: arrowDataBase64,
payloadType: 'b64ipc',
payloadCompression: 'zlib',
callback: (tableInstanceName, error) => {
if (error) console.error('Load error:', error)
else console.log('Table loaded:', tableInstanceName)
},
})
// List all tables
send({
type: 'CATALOG.LIST_TABLES',
callback: (tables) => console.log('Tables:', tables),
})
// Subscribe to table changes with enhanced subscription object
send({
type: 'CATALOG.SUBSCRIBE',
subscription: {
tableSpecName: 'my_table',
onSubscribe: (id: string, tableSpecName: string) => {
console.log(`Subscribed to ${tableSpecName} with ID: ${id}`)
},
onChange: (tableInstanceName: string, tableVersionId: number) => {
console.log(`Table updated: ${tableInstanceName}, version: ${tableVersionId}`)
},
},
})
// Unsubscribe using the subscription ID
send({
type: 'CATALOG.UNSUBSCRIBE',
id: 'subscription_id_here',
})
}The subscription system provides real-time notifications when tables are updated:
// Create a subscription with custom callbacks
const subscription = {
tableSpecName: 'users',
onSubscribe: (id: string, tableSpecName: string) => {
console.log(`Successfully subscribed to ${tableSpecName} with ID: ${id}`)
// Store the subscription ID for later unsubscription
setSubscriptionId(id)
},
onChange: (tableInstanceName: string, tableVersionId: number) => {
console.log(`Table ${tableSpecName} updated to version ${tableVersionId}`)
// Handle table updates - e.g., refresh UI, fetch new data
refreshTableData(tableInstanceName)
},
}
send({
type: 'CATALOG.SUBSCRIBE',
subscription,
})
// Later, unsubscribe using the stored ID
send({
type: 'CATALOG.UNSUBSCRIBE',
id: subscriptionId,
})- Node.js 18+
- pnpm (recommended)
# Install dependencies
pnpm install
# Build the project
pnpm build
# Run tests
pnpm test
# Start development mode
pnpm devThe project includes a React example in examples/react-test/:
cd examples/react-test
pnpm install
pnpm devThis will start a development server with a comprehensive UI for testing all database operations.
Contributions welcome!
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT License - see LICENSE file for details.