@livestore/cli 0.4.0 → 0.5.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/fixtures/mock-config.d.ts +7 -9
- package/dist/__tests__/fixtures/mock-config.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mock-config.js +6 -6
- package/dist/__tests__/fixtures/mock-config.js.map +1 -1
- package/dist/__tests__/sync-operations.test.js +20 -20
- package/dist/__tests__/sync-operations.test.js.map +1 -1
- package/dist/bin.js +6 -5
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +2 -23
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/__fixtures__/mcp-protocol-server.d.ts +2 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.d.ts.map +1 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.js +10 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.js.map +1 -0
- package/dist/commands/import-export.d.ts +5 -19
- package/dist/commands/import-export.d.ts.map +1 -1
- package/dist/commands/import-export.js +12 -12
- package/dist/commands/import-export.js.map +1 -1
- package/dist/commands/mcp-coach.d.ts +8 -7
- package/dist/commands/mcp-coach.d.ts.map +1 -1
- package/dist/commands/mcp-coach.js +10 -10
- package/dist/commands/mcp-coach.js.map +1 -1
- package/dist/commands/mcp-protocols.d.ts +7 -0
- package/dist/commands/mcp-protocols.d.ts.map +1 -0
- package/dist/commands/mcp-protocols.js +12 -0
- package/dist/commands/mcp-protocols.js.map +1 -0
- package/dist/commands/mcp-protocols.test.d.ts +2 -0
- package/dist/commands/mcp-protocols.test.d.ts.map +1 -0
- package/dist/commands/mcp-protocols.test.js +60 -0
- package/dist/commands/mcp-protocols.test.js.map +1 -0
- package/dist/commands/mcp-stdio.test.d.ts +2 -0
- package/dist/commands/mcp-stdio.test.d.ts.map +1 -0
- package/dist/commands/mcp-stdio.test.js +79 -0
- package/dist/commands/mcp-stdio.test.js.map +1 -0
- package/dist/commands/mcp-tool-handlers.d.ts +1 -1
- package/dist/commands/mcp-tool-handlers.d.ts.map +1 -1
- package/dist/commands/mcp-tool-handlers.js +1 -0
- package/dist/commands/mcp-tool-handlers.js.map +1 -1
- package/dist/commands/mcp-tools-defs.d.ts +65 -64
- package/dist/commands/mcp-tools-defs.d.ts.map +1 -1
- package/dist/commands/mcp-tools-defs.js +59 -52
- package/dist/commands/mcp-tools-defs.js.map +1 -1
- package/dist/commands/mcp-tools-defs.test.d.ts +2 -0
- package/dist/commands/mcp-tools-defs.test.d.ts.map +1 -0
- package/dist/commands/mcp-tools-defs.test.js +11 -0
- package/dist/commands/mcp-tools-defs.test.js.map +1 -0
- package/dist/commands/mcp.d.ts +1 -3
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +5 -4
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/new-project.d.ts +19 -27
- package/dist/commands/new-project.d.ts.map +1 -1
- package/dist/commands/new-project.js +17 -16
- package/dist/commands/new-project.js.map +1 -1
- package/dist/mcp-content/architecture.d.ts +1 -1
- package/dist/mcp-content/architecture.d.ts.map +1 -1
- package/dist/mcp-content/architecture.js +3 -3
- package/dist/mcp-content/features.d.ts +1 -1
- package/dist/mcp-content/features.d.ts.map +1 -1
- package/dist/mcp-content/features.js +1 -1
- package/dist/mcp-content/getting-started.d.ts +1 -1
- package/dist/mcp-content/getting-started.d.ts.map +1 -1
- package/dist/mcp-content/getting-started.js +37 -37
- package/dist/mcp-content/overview.d.ts +1 -1
- package/dist/mcp-content/overview.d.ts.map +1 -1
- package/dist/mcp-content/overview.js +6 -6
- package/dist/mcp-content/schemas/blog.d.ts +1 -1
- package/dist/mcp-content/schemas/blog.d.ts.map +1 -1
- package/dist/mcp-content/schemas/blog.js +25 -25
- package/dist/mcp-content/schemas/ecommerce.d.ts +1 -1
- package/dist/mcp-content/schemas/ecommerce.d.ts.map +1 -1
- package/dist/mcp-content/schemas/ecommerce.js +67 -67
- package/dist/mcp-content/schemas/social.d.ts +1 -1
- package/dist/mcp-content/schemas/social.d.ts.map +1 -1
- package/dist/mcp-content/schemas/social.js +48 -48
- package/dist/mcp-content/schemas/todo.d.ts +1 -1
- package/dist/mcp-content/schemas/todo.d.ts.map +1 -1
- package/dist/mcp-content/schemas/todo.js +35 -35
- package/dist/mcp-runtime/runtime.d.ts +2 -2
- package/dist/mcp-runtime/runtime.d.ts.map +1 -1
- package/dist/mcp-runtime/runtime.js +4 -4
- package/dist/mcp-runtime/runtime.js.map +1 -1
- package/dist/module-loader.d.ts +2 -2
- package/dist/module-loader.d.ts.map +1 -1
- package/dist/module-loader.js +6 -6
- package/dist/module-loader.js.map +1 -1
- package/dist/sync-operations.d.ts +24 -30
- package/dist/sync-operations.d.ts.map +1 -1
- package/dist/sync-operations.js +10 -12
- package/dist/sync-operations.js.map +1 -1
- package/package.json +30 -44
- package/src/__tests__/fixtures/mock-config.ts +8 -8
- package/src/__tests__/sync-operations.test.ts +21 -35
- package/src/bin.ts +7 -6
- package/src/cli.ts +1 -1
- package/src/commands/__fixtures__/mcp-protocol-server.ts +12 -0
- package/src/commands/import-export.ts +29 -29
- package/src/commands/mcp-coach.ts +12 -11
- package/src/commands/mcp-protocols.test.ts +76 -0
- package/src/commands/mcp-protocols.ts +12 -0
- package/src/commands/mcp-stdio.test.ts +93 -0
- package/src/commands/mcp-tool-handlers.ts +11 -8
- package/src/commands/mcp-tools-defs.test.ts +13 -0
- package/src/commands/mcp-tools-defs.ts +53 -51
- package/src/commands/mcp.ts +7 -4
- package/src/commands/new-project.ts +49 -36
- package/src/mcp-content/architecture.ts +3 -3
- package/src/mcp-content/features.ts +1 -1
- package/src/mcp-content/getting-started.ts +37 -37
- package/src/mcp-content/overview.ts +6 -6
- package/src/mcp-content/schemas/blog.ts +25 -25
- package/src/mcp-content/schemas/ecommerce.ts +67 -67
- package/src/mcp-content/schemas/social.ts +48 -48
- package/src/mcp-content/schemas/todo.ts +35 -35
- package/src/mcp-runtime/runtime.ts +7 -7
- package/src/module-loader.ts +10 -10
- package/src/sync-operations.ts +16 -16
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const featuresContent = "# LiveStore Features: Production-Ready Local-First Data\n\nLiveStore provides battle-tested primitives for building collaborative, offline-capable applications with the reliability and performance of modern distributed systems.\n\n## \uD83C\uDFE0 Local-First Architecture\n\n### Offline-First Operation\n- **Zero Latency**: All operations execute against local SQLite database\n- **Offline Capable**: Full application functionality without network connectivity\n- **Network Resilient**: Graceful degradation during connectivity issues\n- **Background Sync**: Automatic synchronization when network becomes available\n\n### Immediate Consistency\n- **Optimistic Updates**: UI updates immediately on user action\n- **Rollback on Conflict**: Automatic rollback and retry on merge conflicts\n- **Causal Consistency**: Operations maintain causal ordering across replicas\n\n## \uD83D\uDD04 Event-Driven Synchronization\n\n### Conflict-Free Event Model\n```typescript\n// Events designed for conflict-free merging\nconst events = {\n todoCreated: Events.synced({\n name: 'v1.TodoCreated',\n schema: Schema.Struct({\n id: Schema.String, // Deterministic ordering\n createdAt: Schema.Date, // Timestamp for LWW resolution\n position: Schema.Number // Fractional indexing for reordering\n })\n })\n}\n```\n\n### Sophisticated Merge Strategies\n- **Last-Write-Wins**: Timestamp-based conflict resolution\n- **Semantic Merging**: Application-specific merge logic\n- **Operational Transform**: Real-time collaborative text editing\n- **CRDT Integration**: Conflict-free replicated data types\n\n### Incremental Synchronization\n- **Delta Sync**: Only transmit changes since last synchronization\n- **Vector Clocks**: Efficient causal dependency tracking\n- **Merkle Trees**: Efficient integrity verification\n- **Batch Optimization**: Multiple events in single network round-trip\n\n## \uD83D\uDCCA Reactive Query Engine\n\n### Real-Time Reactivity\n```typescript\n// Queries automatically update when underlying data changes\nconst activeTodos$ = queryDb(\n tables.todos\n .select()\n .where({ completed: false, deletedAt: null })\n .orderBy('position'),\n { label: 'activeTodos' }\n)\n```\n\n### Advanced Query Capabilities\n- **Joins & Aggregations**: Full SQL expressiveness\n- **Reactive Subscriptions**: Automatic UI updates on data changes\n- **Query Optimization**: SQLite query planner with B-tree indexes\n- **Prepared Statements**: Cached query compilation for performance\n\n### Framework Integrations\n- **React**: `useLiveQuery()` hook for reactive components\n- **Vue**: Composables for reactive data binding\n- **Solid**: Reactive primitives integration\n- **Svelte**: Store-based reactive updates\n\n## \uD83D\uDD10 End-to-End Type Safety\n\n### Schema-First Development\n```typescript\n// Type safety from database to UI\nconst todoSchema = State.SQLite.table({\n name: 'todos',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n title: State.SQLite.text(),\n completed: State.SQLite.boolean()\n }\n})\n\n// Fully typed queries\ntype Todo = typeof todoSchema.select.Type // { id: string, title: string, completed: boolean }\n```\n\n### Runtime Validation\n- **Schema Validation**: All events validated against Effect schemas\n- **Migration Safety**: Type-safe schema evolution\n- **Parse Don't Validate**: Schema types flow through entire application\n\n## \uD83C\uDF10 Multi-Platform Support\n\n### Web Platform\n- **Origin Private File System**: Persistent storage in modern browsers\n- **SharedWorker**: Cross-tab synchronization and resource sharing\n- **IndexedDB Fallback**: Compatibility with older browsers\n- **Service Worker**: Background synchronization\n\n### Node.js Platform\n- **File System Storage**: Native SQLite database files\n- **Cluster Coordination**: Multi-process synchronization\n- **HTTP/WebSocket Sync**: Flexible transport protocols\n- **Background Jobs**: Scheduled synchronization tasks\n\n### Mobile Platforms (Expo/React Native)\n- **Native SQLite**: Platform-optimized database performance\n- **Background Sync**: Synchronization during app backgrounding\n- **Push Notifications**: Real-time update notifications\n- **Secure Storage**: Encrypted local data storage\n\n## \uD83D\uDE80 Performance & Scalability\n\n### Query Performance\n- **Sub-millisecond Queries**: Local SQLite performance\n- **Efficient Indexing**: Automatic index recommendations\n- **Result Set Streaming**: Large datasets loaded incrementally\n- **Query Result Caching**: Expensive computations cached\n\n### Memory Management\n- **Lazy Loading**: Data loaded on-demand\n- **Connection Pooling**: Database connections efficiently reused\n- **Event Log Compaction**: Periodic snapshots prevent unbounded growth\n- **Garbage Collection**: Automatic cleanup of obsolete data\n\n### Network Optimization\n- **Compression**: Event payloads compressed for transmission\n- **Request Batching**: Multiple operations in single request\n- **Connection Pooling**: HTTP/WebSocket connections reused\n- **Exponential Backoff**: Intelligent retry strategies\n\n## \uD83C\uDFD7\uFE0F Developer Experience\n\n### Testing & Debugging\n```typescript\n// Built-in testing utilities\nconst testStore = createTestStore(schema)\n\n// Deterministic event replay for testing\nawait testStore.replay([\n events.todoCreated({ id: '1', title: 'Test todo' }),\n events.todoCompleted({ id: '1' })\n])\n```\n\n### Developer Tools\n- **Event Inspector**: Real-time event log visualization\n- **Query Profiler**: Performance analysis for slow queries
|
|
1
|
+
export declare const featuresContent = "# LiveStore Features: Production-Ready Local-First Data\n\nLiveStore provides battle-tested primitives for building collaborative, offline-capable applications with the reliability and performance of modern distributed systems.\n\n## \uD83C\uDFE0 Local-First Architecture\n\n### Offline-First Operation\n- **Zero Latency**: All operations execute against local SQLite database\n- **Offline Capable**: Full application functionality without network connectivity\n- **Network Resilient**: Graceful degradation during connectivity issues\n- **Background Sync**: Automatic synchronization when network becomes available\n\n### Immediate Consistency\n- **Optimistic Updates**: UI updates immediately on user action\n- **Rollback on Conflict**: Automatic rollback and retry on merge conflicts\n- **Causal Consistency**: Operations maintain causal ordering across replicas\n\n## \uD83D\uDD04 Event-Driven Synchronization\n\n### Conflict-Free Event Model\n```typescript\n// Events designed for conflict-free merging\nconst events = {\n todoCreated: Events.synced({\n name: 'v1.TodoCreated',\n schema: Schema.Struct({\n id: Schema.String, // Deterministic ordering\n createdAt: Schema.Date, // Timestamp for LWW resolution\n position: Schema.Number // Fractional indexing for reordering\n })\n })\n}\n```\n\n### Sophisticated Merge Strategies\n- **Last-Write-Wins**: Timestamp-based conflict resolution\n- **Semantic Merging**: Application-specific merge logic\n- **Operational Transform**: Real-time collaborative text editing\n- **CRDT Integration**: Conflict-free replicated data types\n\n### Incremental Synchronization\n- **Delta Sync**: Only transmit changes since last synchronization\n- **Vector Clocks**: Efficient causal dependency tracking\n- **Merkle Trees**: Efficient integrity verification\n- **Batch Optimization**: Multiple events in single network round-trip\n\n## \uD83D\uDCCA Reactive Query Engine\n\n### Real-Time Reactivity\n```typescript\n// Queries automatically update when underlying data changes\nconst activeTodos$ = queryDb(\n tables.todos\n .select()\n .where({ completed: false, deletedAt: null })\n .orderBy('position'),\n { label: 'activeTodos' }\n)\n```\n\n### Advanced Query Capabilities\n- **Joins & Aggregations**: Full SQL expressiveness\n- **Reactive Subscriptions**: Automatic UI updates on data changes\n- **Query Optimization**: SQLite query planner with B-tree indexes\n- **Prepared Statements**: Cached query compilation for performance\n\n### Framework Integrations\n- **React**: `useLiveQuery()` hook for reactive components\n- **Vue**: Composables for reactive data binding\n- **Solid**: Reactive primitives integration\n- **Svelte**: Store-based reactive updates\n\n## \uD83D\uDD10 End-to-End Type Safety\n\n### Schema-First Development\n```typescript\n// Type safety from database to UI\nconst todoSchema = State.SQLite.table({\n name: 'todos',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n title: State.SQLite.text(),\n completed: State.SQLite.boolean()\n }\n})\n\n// Fully typed queries\ntype Todo = typeof todoSchema.select.Type // { id: string, title: string, completed: boolean }\n```\n\n### Runtime Validation\n- **Schema Validation**: All events validated against Effect schemas\n- **Migration Safety**: Type-safe schema evolution\n- **Parse Don't Validate**: Schema types flow through entire application\n\n## \uD83C\uDF10 Multi-Platform Support\n\n### Web Platform\n- **Origin Private File System**: Persistent storage in modern browsers\n- **SharedWorker**: Cross-tab synchronization and resource sharing\n- **IndexedDB Fallback**: Compatibility with older browsers\n- **Service Worker**: Background synchronization\n\n### Node.js Platform\n- **File System Storage**: Native SQLite database files\n- **Cluster Coordination**: Multi-process synchronization\n- **HTTP/WebSocket Sync**: Flexible transport protocols\n- **Background Jobs**: Scheduled synchronization tasks\n\n### Mobile Platforms (Expo/React Native)\n- **Native SQLite**: Platform-optimized database performance\n- **Background Sync**: Synchronization during app backgrounding\n- **Push Notifications**: Real-time update notifications\n- **Secure Storage**: Encrypted local data storage\n\n## \uD83D\uDE80 Performance & Scalability\n\n### Query Performance\n- **Sub-millisecond Queries**: Local SQLite performance\n- **Efficient Indexing**: Automatic index recommendations\n- **Result Set Streaming**: Large datasets loaded incrementally\n- **Query Result Caching**: Expensive computations cached\n\n### Memory Management\n- **Lazy Loading**: Data loaded on-demand\n- **Connection Pooling**: Database connections efficiently reused\n- **Event Log Compaction**: Periodic snapshots prevent unbounded growth\n- **Garbage Collection**: Automatic cleanup of obsolete data\n\n### Network Optimization\n- **Compression**: Event payloads compressed for transmission\n- **Request Batching**: Multiple operations in single request\n- **Connection Pooling**: HTTP/WebSocket connections reused\n- **Exponential Backoff**: Intelligent retry strategies\n\n## \uD83C\uDFD7\uFE0F Developer Experience\n\n### Testing & Debugging\n```typescript\n// Built-in testing utilities\nconst testStore = createTestStore(schema)\n\n// Deterministic event replay for testing\nawait testStore.replay([\n events.todoCreated({ id: '1', title: 'Test todo' }),\n events.todoCompleted({ id: '1' })\n])\n```\n\n### Developer Tools\n- **Event Inspector**: Real-time event log visualization\n- **Query Profiler**: Performance analysis for slow queries\n- **Sync Monitor**: Network synchronization health dashboard\n- **Schema Explorer**: Interactive database schema browsing\n\n### Production Monitoring\n- **Distributed Tracing**: Event propagation across replicas\n- **Performance Metrics**: Query latency and throughput monitoring\n- **Error Tracking**: Comprehensive error reporting and alerting\n- **Health Checks**: Automated system health verification\n\n## \uD83D\uDD12 Security & Privacy\n\n### Data Protection\n- **End-to-End Encryption**: Client-side encryption before transmission\n- **Event Signatures**: Cryptographic integrity verification\n- **Access Control**: Fine-grained permission system\n- **Audit Logging**: Comprehensive security event logging\n\n### Privacy by Design\n- **Local Data Control**: Users maintain complete data ownership\n- **Selective Sync**: Fine-grained control over data sharing\n- **Data Minimization**: Only sync necessary data\n- **GDPR Compliance**: Built-in privacy compliance features\n\nLiveStore combines the reliability of traditional databases with the performance and user experience of local-first applications, enabling you to build applications that users love while maintaining the technical rigor of distributed systems.";
|
|
2
2
|
//# sourceMappingURL=features.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/mcp-content/features.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/mcp-content/features.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,8qNA+KuN,CAAA"}
|
|
@@ -149,7 +149,7 @@ await testStore.replay([
|
|
|
149
149
|
|
|
150
150
|
### Developer Tools
|
|
151
151
|
- **Event Inspector**: Real-time event log visualization
|
|
152
|
-
- **Query Profiler**: Performance analysis for slow queries
|
|
152
|
+
- **Query Profiler**: Performance analysis for slow queries
|
|
153
153
|
- **Sync Monitor**: Network synchronization health dashboard
|
|
154
154
|
- **Schema Explorer**: Interactive database schema browsing
|
|
155
155
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const gettingStartedContent = "# Getting Started with LiveStore\n\nBuild your first local-first application with LiveStore in minutes. This guide walks through creating a real-time collaborative todo app that works offline and syncs when online.\n\n## Quick Start\n\n### 1. Installation\n\n```bash\n# Install LiveStore core\nnpm install @livestore/livestore\n\n# Choose your platform adapter\nnpm install @livestore/adapter-web # For web applications\nnpm install @livestore/adapter-node # For Node.js applications\nnpm install @livestore/adapter-expo # For React Native/Expo apps\n```\n\n### 2. Define Your Schema\n\nLiveStore uses an event-driven architecture where all changes are recorded as immutable events and applied to materialized SQLite tables.\n\n```typescript\n// schema.ts\nimport { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Define your state as SQLite tables\nexport const tables = {\n todos: State.SQLite.table({\n name: 'todos',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n text: State.SQLite.text({ default: '' }),\n completed: State.SQLite.boolean({ default: false }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n // Client-only state (not synced)\n uiState: State.SQLite.clientDocument({\n name: 'uiState',\n schema: Schema.Struct({ \n newTodoText: Schema.String, \n filter: Schema.Literal('all', 'active', 'completed') \n }),\n default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },\n }),\n}\n\n// Define events that represent state changes\nexport const events = {\n todoCreated: Events.synced({\n name: 'v1.TodoCreated',\n schema: Schema.Struct({ \n id: Schema.String, \n text: Schema.String,\n createdAt: Schema.Date \n }),\n }),\n todoCompleted: Events.synced({\n name: 'v1.TodoCompleted',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n todoUncompleted: Events.synced({\n name: 'v1.TodoUncompleted',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n todoDeleted: Events.synced({\n name: 'v1.TodoDeleted',\n schema: Schema.Struct({ id: Schema.String, deletedAt: Schema.Date }),\n }),\n \n // UI state events (local only)\n uiStateSet: tables.uiState.set,\n}\n\n// Materializers map events to state changes\nconst materializers = State.SQLite.materializers(events, {\n 'v1.TodoCreated': ({ id, text, createdAt }) => \n tables.todos.insert({ id, text, completed: false, createdAt }),\n \n 'v1.TodoCompleted': ({ id }) => \n tables.todos.update({ completed: true }).where({ id }),\n \n 'v1.TodoUncompleted': ({ id }) => \n tables.todos.update({ completed: false }).where({ id }),\n \n 'v1.TodoDeleted': ({ id, deletedAt }) => \n tables.todos.update({ deletedAt }).where({ id }),\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n```\n\n### 3. Create Reactive Queries\n\nLiveStore queries automatically update your UI when underlying data changes.\n\n```typescript\n// queries.ts\nimport { queryDb } from '@livestore/livestore'\nimport { tables } from './schema.js'\n\n// Reactive query for all active todos\nexport const activeTodos$ = queryDb(\n tables.todos\n .select()\n .where({ deletedAt: null, completed: false })\n .orderBy('createdAt'),\n { label: 'activeTodos' }\n)\n\n// Reactive query for completed todos\nexport const completedTodos$ = queryDb(\n tables.todos\n .select()\n .where({ deletedAt: null, completed: true })\n .orderBy('createdAt'),\n { label: 'completedTodos' }\n)\n\n// UI state query\nexport const uiState$ = queryDb(\n tables.uiState.get(),\n { label: 'uiState' }\n)\n```\n\n### 4. Connect to Your UI Framework\n\n#### React Integration\n\n```typescript\n// TodoApp.tsx\nimport React from 'react'\nimport { useLiveQuery, dispatchEvent } from '@livestore/react'\nimport { activeTodos$, completedTodos$, uiState$ } from './queries.js'\nimport { events } from './schema.js'\n\nconst TodoApp: React.FC = () => {\n const activeTodos = useLiveQuery(activeTodos$)\n const completedTodos = useLiveQuery(completedTodos$)\n const uiState = useLiveQuery(uiState$)\n \n const createTodo = (text: string) => {\n const id = crypto.randomUUID()\n dispatchEvent(events.todoCreated({ id, text, createdAt: new Date() }))\n }\n \n const toggleTodo = (id: string, completed: boolean) => {\n dispatchEvent(\n completed \n ? events.todoCompleted({ id })\n : events.todoUncompleted({ id })\n )\n }\n \n const deleteTodo = (id: string) => {\n dispatchEvent(events.todoDeleted({ id, deletedAt: new Date() }))\n }\n \n return (\n <div className=\"todo-app\">\n <h1>Local-First Todos</h1>\n \n {/* Add todo form */}\n <form onSubmit={(e) => {\n e.preventDefault()\n const form = e.target as HTMLFormElement\n const input = form.elements.namedItem('todo') as HTMLInputElement\n if (input.value.trim()) {\n createTodo(input.value.trim())\n input.value = ''\n }\n }}>\n <input name=\"todo\" placeholder=\"What needs to be done?\" />\n <button type=\"submit\">Add Todo</button>\n </form>\n \n {/* Active todos */}\n <div className=\"todo-list\">\n <h2>Active ({activeTodos.length})</h2>\n {activeTodos.map(todo => (\n <div key={todo.id} className=\"todo-item\">\n <input \n type=\"checkbox\"\n checked={false}\n onChange={() => toggleTodo(todo.id, true)}\n />\n <span>{todo.text}</span>\n <button onClick={() => deleteTodo(todo.id)}>Delete</button>\n </div>\n ))}\n </div>\n \n {/* Completed todos */}\n <div className=\"todo-list\">\n <h2>Completed ({completedTodos.length})</h2>\n {completedTodos.map(todo => (\n <div key={todo.id} className=\"todo-item completed\">\n <input \n type=\"checkbox\"\n checked={true}\n onChange={() => toggleTodo(todo.id, false)}\n />\n <span>{todo.text}</span>\n <button onClick={() => deleteTodo(todo.id)}>Delete</button>\n </div>\n ))}\n </div>\n </div>\n )\n}\n\nexport default TodoApp\n```\n\n#### Vue Integration\n\n```vue\n<!-- TodoApp.vue -->\n<template>\n <div class=\"todo-app\">\n <h1>Local-First Todos</h1>\n \n <form @submit.prevent=\"createTodo\">\n <input v-model=\"newTodoText\" placeholder=\"What needs to be done?\" />\n <button type=\"submit\">Add Todo</button>\n </form>\n \n <div class=\"todo-list\">\n <h2>Active ({{ activeTodos.length }})</h2>\n <div v-for=\"todo in activeTodos\" :key=\"todo.id\" class=\"todo-item\">\n <input \n type=\"checkbox\"\n :checked=\"false\"\n @change=\"toggleTodo(todo.id, true)\"\n />\n <span>{{ todo.text }}</span>\n <button @click=\"deleteTodo(todo.id)\">Delete</button>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { useLiveQuery, dispatchEvent } from '@livestore/vue'\nimport { activeTodos$ } from './queries.js'\nimport { events } from './schema.js'\n\nconst activeTodos = useLiveQuery(activeTodos$)\nconst newTodoText = ref('')\n\nconst createTodo = () => {\n if (newTodoText.value.trim()) {\n const id = crypto.randomUUID()\n dispatchEvent(events.todoCreated({ \n id, \n text: newTodoText.value.trim(), \n createdAt: new Date() \n }))\n newTodoText.value = ''\n }\n}\n\nconst toggleTodo = (id: string, completed: boolean) => {\n dispatchEvent(\n completed \n ? events.todoCompleted({ id })\n : events.todoUncompleted({ id })\n )\n}\n\nconst deleteTodo = (id: string) => {\n dispatchEvent(events.todoDeleted({ id, deletedAt: new Date() }))\n}\n</script>\n```\n\n### 5. Initialize Your Application\n\n```typescript\n// main.ts\nimport { LiveStore } from '@livestore/livestore'\nimport { WebAdapter } from '@livestore/adapter-web'\nimport { schema } from './schema.js'\n\n// Initialize LiveStore with your schema\nconst liveStore = LiveStore.create({\n schema,\n adapter: WebAdapter({\n databaseName: 'todo-app',\n // Optional: Add sync configuration\n sync: {\n url: 'wss://your-sync-server.com',\n auth: { token: 'your-auth-token' }\n }\n })\n})\n\n// Start your application\nconst app = document.getElementById('app')\nif (app) {\n // Your framework-specific initialization\n // React: createRoot(app).render(<TodoApp />)\n // Vue: createApp(TodoApp).mount(app)\n}\n```\n\n## Advanced Features\n\n### Offline Support\n\nYour app automatically works offline. All operations execute against the local database, and changes sync when connectivity returns.\n\n```typescript\n// Check online status\nconst isOnline$ = queryDb(\n LiveStore.connectionStatus(),\n { label: 'connectionStatus' }\n)\n```\n\n### Real-Time Collaboration\n\nMultiple users can collaborate in real-time. Conflicts are automatically resolved using last-write-wins or custom merge strategies.\n\n```typescript\n// Custom conflict resolution\nconst materializers = State.SQLite.materializers(events, {\n 'v1.TodoTextChanged': ({ id, text, editedAt }) => \n // Use timestamp for conflict resolution\n tables.todos\n .update({ text, editedAt })\n .where({ id })\n .and(tables.todos.column('editedAt').lt(editedAt))\n})\n```\n\n### Testing\n\n```typescript\n// todo.test.ts\nimport { createTestStore } from '@livestore/testing'\nimport { schema, events } from './schema.js'\n\ntest('creating and completing todos', async () => {\n const store = createTestStore(schema)\n \n // Dispatch events\n await store.dispatch([\n events.todoCreated({ id: '1', text: 'Test todo', createdAt: new Date() }),\n events.todoCompleted({ id: '1' })\n ])\n \n // Query final state\n const completedTodos = await store.query(\n tables.todos.select().where({ completed: true })\n )\n \n expect(completedTodos).toHaveLength(1)\n expect(completedTodos[0].text).toBe('Test todo')\n})\n```\n\n## Next Steps\n\n### Production Deployment\n1. **Set up sync server**: Deploy LiveStore sync server for real-time collaboration\n2. **Configure authentication**: Add user authentication and authorization\n3. **Add monitoring**: Set up distributed tracing and performance monitoring\n4. **Optimize performance**: Add indexes and query optimization\n\n### Advanced Patterns\n- **Multi-user collaboration**: User permissions and access control\n- **Rich text editing**: Operational transforms for collaborative editing\n- **File synchronization**: Binary data and file attachment handling\n- **Schema migrations**: Evolving your data model over time\n\n### Platform-Specific Guides\n- **Web deployment**: Service workers, PWA configuration, OPFS optimization\n- **Mobile apps**: Background sync, push notifications, native storage\n- **Desktop apps**: Electron integration, native file system access\n\n## Examples Repository\n\nExplore complete working examples:\n- **TodoMVC**: Classic todo app with real-time sync\n- **Collaborative Editor**: Rich text editing with operational transforms\n- **Chat Application**: Real-time messaging with presence indicators\n- **E-commerce**: Product catalog with shopping cart and orders\n\n```bash\n# Clone examples repository\ngit clone https://github.com/livestorejs/examples.git\ncd examples/web-todomvc\nnpm install && npm run dev\n```\n\nVisit [docs.livestore.dev](https://docs.livestore.dev) for comprehensive documentation, API reference, and advanced patterns.";
|
|
1
|
+
export declare const gettingStartedContent = "# Getting Started with LiveStore\n\nBuild your first local-first application with LiveStore in minutes. This guide walks through creating a real-time collaborative todo app that works offline and syncs when online.\n\n## Quick Start\n\n### 1. Installation\n\n```bash\n# Install LiveStore core\nnpm install @livestore/livestore\n\n# Choose your platform adapter\nnpm install @livestore/adapter-web # For web applications\nnpm install @livestore/adapter-node # For Node.js applications\nnpm install @livestore/adapter-expo # For React Native/Expo apps\n```\n\n### 2. Define Your Schema\n\nLiveStore uses an event-driven architecture where all changes are recorded as immutable events and applied to materialized SQLite tables.\n\n```typescript\n// schema.ts\nimport { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Define your state as SQLite tables\nexport const tables = {\n todos: State.SQLite.table({\n name: 'todos',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n text: State.SQLite.text({ default: '' }),\n completed: State.SQLite.boolean({ default: false }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n // Client-only state (not synced)\n uiState: State.SQLite.clientDocument({\n name: 'uiState',\n schema: Schema.Struct({\n newTodoText: Schema.String,\n filter: Schema.Literal('all', 'active', 'completed')\n }),\n default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },\n }),\n}\n\n// Define events that represent state changes\nexport const events = {\n todoCreated: Events.synced({\n name: 'v1.TodoCreated',\n schema: Schema.Struct({\n id: Schema.String,\n text: Schema.String,\n createdAt: Schema.Date\n }),\n }),\n todoCompleted: Events.synced({\n name: 'v1.TodoCompleted',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n todoUncompleted: Events.synced({\n name: 'v1.TodoUncompleted',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n todoDeleted: Events.synced({\n name: 'v1.TodoDeleted',\n schema: Schema.Struct({ id: Schema.String, deletedAt: Schema.Date }),\n }),\n\n // UI state events (local only)\n uiStateSet: tables.uiState.set,\n}\n\n// Materializers map events to state changes\nconst materializers = State.SQLite.materializers(events, {\n 'v1.TodoCreated': ({ id, text, createdAt }) =>\n tables.todos.insert({ id, text, completed: false, createdAt }),\n\n 'v1.TodoCompleted': ({ id }) =>\n tables.todos.update({ completed: true }).where({ id }),\n\n 'v1.TodoUncompleted': ({ id }) =>\n tables.todos.update({ completed: false }).where({ id }),\n\n 'v1.TodoDeleted': ({ id, deletedAt }) =>\n tables.todos.update({ deletedAt }).where({ id }),\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n```\n\n### 3. Create Reactive Queries\n\nLiveStore queries automatically update your UI when underlying data changes.\n\n```typescript\n// queries.ts\nimport { queryDb } from '@livestore/livestore'\nimport { tables } from './schema.js'\n\n// Reactive query for all active todos\nexport const activeTodos$ = queryDb(\n tables.todos\n .select()\n .where({ deletedAt: null, completed: false })\n .orderBy('createdAt'),\n { label: 'activeTodos' }\n)\n\n// Reactive query for completed todos\nexport const completedTodos$ = queryDb(\n tables.todos\n .select()\n .where({ deletedAt: null, completed: true })\n .orderBy('createdAt'),\n { label: 'completedTodos' }\n)\n\n// UI state query\nexport const uiState$ = queryDb(\n tables.uiState.get(),\n { label: 'uiState' }\n)\n```\n\n### 4. Connect to Your UI Framework\n\n#### React Integration\n\n```typescript\n// TodoApp.tsx\nimport React from 'react'\nimport { useLiveQuery, dispatchEvent } from '@livestore/react'\nimport { activeTodos$, completedTodos$, uiState$ } from './queries.js'\nimport { events } from './schema.js'\n\nconst TodoApp: React.FC = () => {\n const activeTodos = useLiveQuery(activeTodos$)\n const completedTodos = useLiveQuery(completedTodos$)\n const uiState = useLiveQuery(uiState$)\n\n const createTodo = (text: string) => {\n const id = crypto.randomUUID()\n dispatchEvent(events.todoCreated({ id, text, createdAt: new Date() }))\n }\n\n const toggleTodo = (id: string, completed: boolean) => {\n dispatchEvent(\n completed\n ? events.todoCompleted({ id })\n : events.todoUncompleted({ id })\n )\n }\n\n const deleteTodo = (id: string) => {\n dispatchEvent(events.todoDeleted({ id, deletedAt: new Date() }))\n }\n\n return (\n <div className=\"todo-app\">\n <h1>Local-First Todos</h1>\n\n {/* Add todo form */}\n <form onSubmit={(e) => {\n e.preventDefault()\n const form = e.target as HTMLFormElement\n const input = form.elements.namedItem('todo') as HTMLInputElement\n if (input.value.trim()) {\n createTodo(input.value.trim())\n input.value = ''\n }\n }}>\n <input name=\"todo\" placeholder=\"What needs to be done?\" />\n <button type=\"submit\">Add Todo</button>\n </form>\n\n {/* Active todos */}\n <div className=\"todo-list\">\n <h2>Active ({activeTodos.length})</h2>\n {activeTodos.map(todo => (\n <div key={todo.id} className=\"todo-item\">\n <input\n type=\"checkbox\"\n checked={false}\n onChange={() => toggleTodo(todo.id, true)}\n />\n <span>{todo.text}</span>\n <button onClick={() => deleteTodo(todo.id)}>Delete</button>\n </div>\n ))}\n </div>\n\n {/* Completed todos */}\n <div className=\"todo-list\">\n <h2>Completed ({completedTodos.length})</h2>\n {completedTodos.map(todo => (\n <div key={todo.id} className=\"todo-item completed\">\n <input\n type=\"checkbox\"\n checked={true}\n onChange={() => toggleTodo(todo.id, false)}\n />\n <span>{todo.text}</span>\n <button onClick={() => deleteTodo(todo.id)}>Delete</button>\n </div>\n ))}\n </div>\n </div>\n )\n}\n\nexport default TodoApp\n```\n\n#### Vue Integration\n\n```vue\n<!-- TodoApp.vue -->\n<template>\n <div class=\"todo-app\">\n <h1>Local-First Todos</h1>\n\n <form @submit.prevent=\"createTodo\">\n <input v-model=\"newTodoText\" placeholder=\"What needs to be done?\" />\n <button type=\"submit\">Add Todo</button>\n </form>\n\n <div class=\"todo-list\">\n <h2>Active ({{ activeTodos.length }})</h2>\n <div v-for=\"todo in activeTodos\" :key=\"todo.id\" class=\"todo-item\">\n <input\n type=\"checkbox\"\n :checked=\"false\"\n @change=\"toggleTodo(todo.id, true)\"\n />\n <span>{{ todo.text }}</span>\n <button @click=\"deleteTodo(todo.id)\">Delete</button>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { useLiveQuery, dispatchEvent } from '@livestore/vue'\nimport { activeTodos$ } from './queries.js'\nimport { events } from './schema.js'\n\nconst activeTodos = useLiveQuery(activeTodos$)\nconst newTodoText = ref('')\n\nconst createTodo = () => {\n if (newTodoText.value.trim()) {\n const id = crypto.randomUUID()\n dispatchEvent(events.todoCreated({\n id,\n text: newTodoText.value.trim(),\n createdAt: new Date()\n }))\n newTodoText.value = ''\n }\n}\n\nconst toggleTodo = (id: string, completed: boolean) => {\n dispatchEvent(\n completed\n ? events.todoCompleted({ id })\n : events.todoUncompleted({ id })\n )\n}\n\nconst deleteTodo = (id: string) => {\n dispatchEvent(events.todoDeleted({ id, deletedAt: new Date() }))\n}\n</script>\n```\n\n### 5. Initialize Your Application\n\n```typescript\n// main.ts\nimport { LiveStore } from '@livestore/livestore'\nimport { WebAdapter } from '@livestore/adapter-web'\nimport { schema } from './schema.js'\n\n// Initialize LiveStore with your schema\nconst liveStore = LiveStore.create({\n schema,\n adapter: WebAdapter({\n databaseName: 'todo-app',\n // Optional: Add sync configuration\n sync: {\n url: 'wss://your-sync-server.com',\n auth: { token: 'your-auth-token' }\n }\n })\n})\n\n// Start your application\nconst app = document.getElementById('app')\nif (app) {\n // Your framework-specific initialization\n // React: createRoot(app).render(<TodoApp />)\n // Vue: createApp(TodoApp).mount(app)\n}\n```\n\n## Advanced Features\n\n### Offline Support\n\nYour app automatically works offline. All operations execute against the local database, and changes sync when connectivity returns.\n\n```typescript\n// Check online status\nconst isOnline$ = queryDb(\n LiveStore.connectionStatus(),\n { label: 'connectionStatus' }\n)\n```\n\n### Real-Time Collaboration\n\nMultiple users can collaborate in real-time. Conflicts are automatically resolved using last-write-wins or custom merge strategies.\n\n```typescript\n// Custom conflict resolution\nconst materializers = State.SQLite.materializers(events, {\n 'v1.TodoTextChanged': ({ id, text, editedAt }) =>\n // Use timestamp for conflict resolution\n tables.todos\n .update({ text, editedAt })\n .where({ id })\n .and(tables.todos.column('editedAt').lt(editedAt))\n})\n```\n\n### Testing\n\n```typescript\n// todo.test.ts\nimport { createTestStore } from '@livestore/testing'\nimport { schema, events } from './schema.js'\n\ntest('creating and completing todos', async () => {\n const store = createTestStore(schema)\n\n // Dispatch events\n await store.dispatch([\n events.todoCreated({ id: '1', text: 'Test todo', createdAt: new Date() }),\n events.todoCompleted({ id: '1' })\n ])\n\n // Query final state\n const completedTodos = await store.query(\n tables.todos.select().where({ completed: true })\n )\n\n expect(completedTodos).toHaveLength(1)\n expect(completedTodos[0].text).toBe('Test todo')\n})\n```\n\n## Next Steps\n\n### Production Deployment\n1. **Set up sync server**: Deploy LiveStore sync server for real-time collaboration\n2. **Configure authentication**: Add user authentication and authorization\n3. **Add monitoring**: Set up distributed tracing and performance monitoring\n4. **Optimize performance**: Add indexes and query optimization\n\n### Advanced Patterns\n- **Multi-user collaboration**: User permissions and access control\n- **Rich text editing**: Operational transforms for collaborative editing\n- **File synchronization**: Binary data and file attachment handling\n- **Schema migrations**: Evolving your data model over time\n\n### Platform-Specific Guides\n- **Web deployment**: Service workers, PWA configuration, OPFS optimization\n- **Mobile apps**: Background sync, push notifications, native storage\n- **Desktop apps**: Electron integration, native file system access\n\n## Examples Repository\n\nExplore complete working examples:\n- **TodoMVC**: Classic todo app with real-time sync\n- **Collaborative Editor**: Rich text editing with operational transforms\n- **Chat Application**: Real-time messaging with presence indicators\n- **E-commerce**: Product catalog with shopping cart and orders\n\n```bash\n# Clone examples repository\ngit clone https://github.com/livestorejs/examples.git\ncd examples/web-todomvc\nnpm install && npm run dev\n```\n\nVisit [docs.livestore.dev](https://docs.livestore.dev) for comprehensive documentation, API reference, and advanced patterns.";
|
|
2
2
|
//# sourceMappingURL=getting-started.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getting-started.d.ts","sourceRoot":"","sources":["../../src/mcp-content/getting-started.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"getting-started.d.ts","sourceRoot":"","sources":["../../src/mcp-content/getting-started.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,gkXAmZ4F,CAAA"}
|
|
@@ -36,13 +36,13 @@ export const tables = {
|
|
|
36
36
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
37
37
|
},
|
|
38
38
|
}),
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// Client-only state (not synced)
|
|
41
41
|
uiState: State.SQLite.clientDocument({
|
|
42
42
|
name: 'uiState',
|
|
43
|
-
schema: Schema.Struct({
|
|
44
|
-
newTodoText: Schema.String,
|
|
45
|
-
filter: Schema.Literal('all', 'active', 'completed')
|
|
43
|
+
schema: Schema.Struct({
|
|
44
|
+
newTodoText: Schema.String,
|
|
45
|
+
filter: Schema.Literal('all', 'active', 'completed')
|
|
46
46
|
}),
|
|
47
47
|
default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },
|
|
48
48
|
}),
|
|
@@ -52,10 +52,10 @@ export const tables = {
|
|
|
52
52
|
export const events = {
|
|
53
53
|
todoCreated: Events.synced({
|
|
54
54
|
name: 'v1.TodoCreated',
|
|
55
|
-
schema: Schema.Struct({
|
|
56
|
-
id: Schema.String,
|
|
55
|
+
schema: Schema.Struct({
|
|
56
|
+
id: Schema.String,
|
|
57
57
|
text: Schema.String,
|
|
58
|
-
createdAt: Schema.Date
|
|
58
|
+
createdAt: Schema.Date
|
|
59
59
|
}),
|
|
60
60
|
}),
|
|
61
61
|
todoCompleted: Events.synced({
|
|
@@ -70,23 +70,23 @@ export const events = {
|
|
|
70
70
|
name: 'v1.TodoDeleted',
|
|
71
71
|
schema: Schema.Struct({ id: Schema.String, deletedAt: Schema.Date }),
|
|
72
72
|
}),
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
// UI state events (local only)
|
|
75
75
|
uiStateSet: tables.uiState.set,
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// Materializers map events to state changes
|
|
79
79
|
const materializers = State.SQLite.materializers(events, {
|
|
80
|
-
'v1.TodoCreated': ({ id, text, createdAt }) =>
|
|
80
|
+
'v1.TodoCreated': ({ id, text, createdAt }) =>
|
|
81
81
|
tables.todos.insert({ id, text, completed: false, createdAt }),
|
|
82
|
-
|
|
83
|
-
'v1.TodoCompleted': ({ id }) =>
|
|
82
|
+
|
|
83
|
+
'v1.TodoCompleted': ({ id }) =>
|
|
84
84
|
tables.todos.update({ completed: true }).where({ id }),
|
|
85
|
-
|
|
86
|
-
'v1.TodoUncompleted': ({ id }) =>
|
|
85
|
+
|
|
86
|
+
'v1.TodoUncompleted': ({ id }) =>
|
|
87
87
|
tables.todos.update({ completed: false }).where({ id }),
|
|
88
|
-
|
|
89
|
-
'v1.TodoDeleted': ({ id, deletedAt }) =>
|
|
88
|
+
|
|
89
|
+
'v1.TodoDeleted': ({ id, deletedAt }) =>
|
|
90
90
|
tables.todos.update({ deletedAt }).where({ id }),
|
|
91
91
|
})
|
|
92
92
|
|
|
@@ -144,28 +144,28 @@ const TodoApp: React.FC = () => {
|
|
|
144
144
|
const activeTodos = useLiveQuery(activeTodos$)
|
|
145
145
|
const completedTodos = useLiveQuery(completedTodos$)
|
|
146
146
|
const uiState = useLiveQuery(uiState$)
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
const createTodo = (text: string) => {
|
|
149
149
|
const id = crypto.randomUUID()
|
|
150
150
|
dispatchEvent(events.todoCreated({ id, text, createdAt: new Date() }))
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
const toggleTodo = (id: string, completed: boolean) => {
|
|
154
154
|
dispatchEvent(
|
|
155
|
-
completed
|
|
155
|
+
completed
|
|
156
156
|
? events.todoCompleted({ id })
|
|
157
157
|
: events.todoUncompleted({ id })
|
|
158
158
|
)
|
|
159
159
|
}
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
const deleteTodo = (id: string) => {
|
|
162
162
|
dispatchEvent(events.todoDeleted({ id, deletedAt: new Date() }))
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
return (
|
|
166
166
|
<div className="todo-app">
|
|
167
167
|
<h1>Local-First Todos</h1>
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
{/* Add todo form */}
|
|
170
170
|
<form onSubmit={(e) => {
|
|
171
171
|
e.preventDefault()
|
|
@@ -179,13 +179,13 @@ const TodoApp: React.FC = () => {
|
|
|
179
179
|
<input name="todo" placeholder="What needs to be done?" />
|
|
180
180
|
<button type="submit">Add Todo</button>
|
|
181
181
|
</form>
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
{/* Active todos */}
|
|
184
184
|
<div className="todo-list">
|
|
185
185
|
<h2>Active ({activeTodos.length})</h2>
|
|
186
186
|
{activeTodos.map(todo => (
|
|
187
187
|
<div key={todo.id} className="todo-item">
|
|
188
|
-
<input
|
|
188
|
+
<input
|
|
189
189
|
type="checkbox"
|
|
190
190
|
checked={false}
|
|
191
191
|
onChange={() => toggleTodo(todo.id, true)}
|
|
@@ -195,13 +195,13 @@ const TodoApp: React.FC = () => {
|
|
|
195
195
|
</div>
|
|
196
196
|
))}
|
|
197
197
|
</div>
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
{/* Completed todos */}
|
|
200
200
|
<div className="todo-list">
|
|
201
201
|
<h2>Completed ({completedTodos.length})</h2>
|
|
202
202
|
{completedTodos.map(todo => (
|
|
203
203
|
<div key={todo.id} className="todo-item completed">
|
|
204
|
-
<input
|
|
204
|
+
<input
|
|
205
205
|
type="checkbox"
|
|
206
206
|
checked={true}
|
|
207
207
|
onChange={() => toggleTodo(todo.id, false)}
|
|
@@ -225,16 +225,16 @@ export default TodoApp
|
|
|
225
225
|
<template>
|
|
226
226
|
<div class="todo-app">
|
|
227
227
|
<h1>Local-First Todos</h1>
|
|
228
|
-
|
|
228
|
+
|
|
229
229
|
<form @submit.prevent="createTodo">
|
|
230
230
|
<input v-model="newTodoText" placeholder="What needs to be done?" />
|
|
231
231
|
<button type="submit">Add Todo</button>
|
|
232
232
|
</form>
|
|
233
|
-
|
|
233
|
+
|
|
234
234
|
<div class="todo-list">
|
|
235
235
|
<h2>Active ({{ activeTodos.length }})</h2>
|
|
236
236
|
<div v-for="todo in activeTodos" :key="todo.id" class="todo-item">
|
|
237
|
-
<input
|
|
237
|
+
<input
|
|
238
238
|
type="checkbox"
|
|
239
239
|
:checked="false"
|
|
240
240
|
@change="toggleTodo(todo.id, true)"
|
|
@@ -258,10 +258,10 @@ const newTodoText = ref('')
|
|
|
258
258
|
const createTodo = () => {
|
|
259
259
|
if (newTodoText.value.trim()) {
|
|
260
260
|
const id = crypto.randomUUID()
|
|
261
|
-
dispatchEvent(events.todoCreated({
|
|
262
|
-
id,
|
|
263
|
-
text: newTodoText.value.trim(),
|
|
264
|
-
createdAt: new Date()
|
|
261
|
+
dispatchEvent(events.todoCreated({
|
|
262
|
+
id,
|
|
263
|
+
text: newTodoText.value.trim(),
|
|
264
|
+
createdAt: new Date()
|
|
265
265
|
}))
|
|
266
266
|
newTodoText.value = ''
|
|
267
267
|
}
|
|
@@ -269,7 +269,7 @@ const createTodo = () => {
|
|
|
269
269
|
|
|
270
270
|
const toggleTodo = (id: string, completed: boolean) => {
|
|
271
271
|
dispatchEvent(
|
|
272
|
-
completed
|
|
272
|
+
completed
|
|
273
273
|
? events.todoCompleted({ id })
|
|
274
274
|
: events.todoUncompleted({ id })
|
|
275
275
|
)
|
|
@@ -332,7 +332,7 @@ Multiple users can collaborate in real-time. Conflicts are automatically resolve
|
|
|
332
332
|
\`\`\`typescript
|
|
333
333
|
// Custom conflict resolution
|
|
334
334
|
const materializers = State.SQLite.materializers(events, {
|
|
335
|
-
'v1.TodoTextChanged': ({ id, text, editedAt }) =>
|
|
335
|
+
'v1.TodoTextChanged': ({ id, text, editedAt }) =>
|
|
336
336
|
// Use timestamp for conflict resolution
|
|
337
337
|
tables.todos
|
|
338
338
|
.update({ text, editedAt })
|
|
@@ -350,18 +350,18 @@ import { schema, events } from './schema.js'
|
|
|
350
350
|
|
|
351
351
|
test('creating and completing todos', async () => {
|
|
352
352
|
const store = createTestStore(schema)
|
|
353
|
-
|
|
353
|
+
|
|
354
354
|
// Dispatch events
|
|
355
355
|
await store.dispatch([
|
|
356
356
|
events.todoCreated({ id: '1', text: 'Test todo', createdAt: new Date() }),
|
|
357
357
|
events.todoCompleted({ id: '1' })
|
|
358
358
|
])
|
|
359
|
-
|
|
359
|
+
|
|
360
360
|
// Query final state
|
|
361
361
|
const completedTodos = await store.query(
|
|
362
362
|
tables.todos.select().where({ completed: true })
|
|
363
363
|
)
|
|
364
|
-
|
|
364
|
+
|
|
365
365
|
expect(completedTodos).toHaveLength(1)
|
|
366
366
|
expect(completedTodos[0].text).toBe('Test todo')
|
|
367
367
|
})
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const overviewContent = "# LiveStore: Local-First Data Platform\n\nLiveStore is a production-ready local-first data platform that combines the immediate responsiveness of local SQLite databases with the collaborative power of real-time synchronization. Built on distributed systems principles from Martin Kleppmann's research, LiveStore enables applications that work seamlessly offline and sync reliably when online.\n\n## Core Philosophy\n\n**Local-First Principles**: Your data lives primarily on your device, not in the cloud. Applications respond immediately to user actions without network round-trips, providing the smooth experience users expect from native applications.\n\n**Collaborative by Design**: Real-time collaboration isn't an afterthought \u2013 it's built into the foundation. Multiple users can work together seamlessly with automatic conflict resolution and eventual consistency guarantees.\n\n**Reliability Through Event Sourcing**: All changes are captured as immutable events, providing a complete audit trail and enabling powerful features like undo/redo, time travel debugging, and deterministic testing.\n\n## What Makes LiveStore Different\n\n### \uD83D\uDCBE Local SQLite Performance\n- **Sub-millisecond queries** from local SQLite database\n- **Complex joins and aggregations** with full SQL expressiveness\n- **Reactive queries** that automatically update your UI\n- **ACID transactions** for data integrity\n\n### \uD83C\uDF10 Distributed Systems Reliability\n- **Conflict-free synchronization** using CRDT-inspired merge strategies\n- **Causal consistency** with vector clocks and event ordering\n- **Network partition tolerance** \u2013 works offline, syncs when online\n- **Eventually consistent** convergence across all replicas\n\n### \uD83D\uDD10 End-to-End Type Safety\n- **Schema-first development** with Effect-based validation\n- **Compile-time query validation** prevents runtime errors
|
|
1
|
+
export declare const overviewContent = "# LiveStore: Local-First Data Platform\n\nLiveStore is a production-ready local-first data platform that combines the immediate responsiveness of local SQLite databases with the collaborative power of real-time synchronization. Built on distributed systems principles from Martin Kleppmann's research, LiveStore enables applications that work seamlessly offline and sync reliably when online.\n\n## Core Philosophy\n\n**Local-First Principles**: Your data lives primarily on your device, not in the cloud. Applications respond immediately to user actions without network round-trips, providing the smooth experience users expect from native applications.\n\n**Collaborative by Design**: Real-time collaboration isn't an afterthought \u2013 it's built into the foundation. Multiple users can work together seamlessly with automatic conflict resolution and eventual consistency guarantees.\n\n**Reliability Through Event Sourcing**: All changes are captured as immutable events, providing a complete audit trail and enabling powerful features like undo/redo, time travel debugging, and deterministic testing.\n\n## What Makes LiveStore Different\n\n### \uD83D\uDCBE Local SQLite Performance\n- **Sub-millisecond queries** from local SQLite database\n- **Complex joins and aggregations** with full SQL expressiveness\n- **Reactive queries** that automatically update your UI\n- **ACID transactions** for data integrity\n\n### \uD83C\uDF10 Distributed Systems Reliability\n- **Conflict-free synchronization** using CRDT-inspired merge strategies\n- **Causal consistency** with vector clocks and event ordering\n- **Network partition tolerance** \u2013 works offline, syncs when online\n- **Eventually consistent** convergence across all replicas\n\n### \uD83D\uDD10 End-to-End Type Safety\n- **Schema-first development** with Effect-based validation\n- **Compile-time query validation** prevents runtime errors\n- **Automatic TypeScript generation** from schema definitions\n- **Runtime safety** with comprehensive input validation\n\n### \uD83C\uDFD7\uFE0F Framework Agnostic\n- **React, Vue, Solid, Svelte** \u2013 use with any frontend framework\n- **Web, Node.js, React Native** \u2013 deploy anywhere JavaScript runs\n- **Consistent API** across all platforms and frameworks\n\n## Real-World Use Cases\n\n### \uD83D\uDCDD Collaborative Applications\n- **Document editors** with real-time collaboration (Google Docs-style)\n- **Project management** tools with team coordination\n- **Design tools** with multiplayer editing capabilities\n- **Chat applications** with offline message queuing\n\n### \uD83D\uDCF1 Mobile-First Applications\n- **Field service apps** that work in areas with poor connectivity\n- **Healthcare applications** with sensitive data that must stay local\n- **Educational apps** for students in low-connectivity environments\n- **Financial apps** requiring immediate transaction feedback\n\n### \uD83C\uDFEE Enterprise Applications\n- **CRM systems** with offline sales capability\n- **Inventory management** with real-time stock updates\n- **Customer service** tools with offline case management\n- **Analytics dashboards** with local data caching\n\n## Architecture at a Glance\n\n```mermaid\ngraph TB\n UI[UI Framework] --> Queries[Reactive Queries]\n UI --> Events[Event Dispatch]\n\n Events --> EventLog[Event Log]\n EventLog --> Materializers[Materializers]\n Materializers --> SQLite[SQLite Database]\n Queries --> SQLite\n\n EventLog --> Sync[Sync Engine]\n Sync --> Network[Network Layer]\n Network --> Server[Sync Server]\n\n Server --> OtherClients[Other Clients]\n OtherClients --> Server\n Server --> Network\n```\n\n1. **UI Layer**: Framework-specific bindings (React, Vue, etc.)\n2. **Query Layer**: Reactive SQL queries with automatic UI updates\n3. **Event Layer**: Immutable event log with schema validation\n4. **Materialization**: Events applied to SQLite tables via materializers\n5. **Synchronization**: Conflict-free replication across devices/users\n6. **Storage**: Local SQLite database for immediate data access\n\n## Key Technical Innovations\n\n### Event-Driven State Management\nUnlike traditional ORMs that hide change tracking, LiveStore makes all state changes explicit through events. This provides:\n- **Deterministic state updates** that can be tested and debugged\n- **Conflict resolution** through event reordering and semantic merging\n- **Time travel** capabilities for debugging and feature development\n- **Audit trails** for compliance and data governance\n\n### Sophisticated Conflict Resolution\nLiveStore handles conflicts intelligently using multiple strategies:\n- **Last-Write-Wins**: Simple timestamp-based resolution\n- **Semantic Merging**: Application-specific conflict resolution logic\n- **Operational Transforms**: For real-time collaborative text editing\n- **CRDT Integration**: Conflict-free data types for specific use cases\n\n### Performance Optimization\n- **Query compilation caching** for repeated queries\n- **Reactive dependency tracking** to minimize unnecessary updates\n- **Incremental synchronization** to reduce network overhead\n- **Background processing** to keep the UI thread responsive\n\n## Production Ready\n\nLiveStore is designed for production applications with enterprise-grade requirements:\n\n- **Security**: End-to-end encryption, event signatures, access control\n- **Observability**: Distributed tracing, performance monitoring, health checks\n- **Scalability**: Horizontal scaling, connection pooling, data partitioning\n- **Reliability**: Automatic retries, circuit breakers, graceful degradation\n\nWhether you're building a simple todo app or a complex collaborative platform, LiveStore provides the foundation for applications that users love \u2013 fast, reliable, and always available.\n\n**Ready to get started?** Check out our [Getting Started Guide](./getting-started) or explore our [Example Applications](https://github.com/livestorejs/examples).";
|
|
2
2
|
//# sourceMappingURL=overview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../src/mcp-content/overview.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../src/mcp-content/overview.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,u3LAsHuI,CAAA"}
|
|
@@ -26,7 +26,7 @@ LiveStore is a production-ready local-first data platform that combines the imme
|
|
|
26
26
|
|
|
27
27
|
### 🔐 End-to-End Type Safety
|
|
28
28
|
- **Schema-first development** with Effect-based validation
|
|
29
|
-
- **Compile-time query validation** prevents runtime errors
|
|
29
|
+
- **Compile-time query validation** prevents runtime errors
|
|
30
30
|
- **Automatic TypeScript generation** from schema definitions
|
|
31
31
|
- **Runtime safety** with comprehensive input validation
|
|
32
32
|
|
|
@@ -43,7 +43,7 @@ LiveStore is a production-ready local-first data platform that combines the imme
|
|
|
43
43
|
- **Design tools** with multiplayer editing capabilities
|
|
44
44
|
- **Chat applications** with offline message queuing
|
|
45
45
|
|
|
46
|
-
### 📱 Mobile-First Applications
|
|
46
|
+
### 📱 Mobile-First Applications
|
|
47
47
|
- **Field service apps** that work in areas with poor connectivity
|
|
48
48
|
- **Healthcare applications** with sensitive data that must stay local
|
|
49
49
|
- **Educational apps** for students in low-connectivity environments
|
|
@@ -61,16 +61,16 @@ LiveStore is a production-ready local-first data platform that combines the imme
|
|
|
61
61
|
graph TB
|
|
62
62
|
UI[UI Framework] --> Queries[Reactive Queries]
|
|
63
63
|
UI --> Events[Event Dispatch]
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
Events --> EventLog[Event Log]
|
|
66
66
|
EventLog --> Materializers[Materializers]
|
|
67
67
|
Materializers --> SQLite[SQLite Database]
|
|
68
68
|
Queries --> SQLite
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
EventLog --> Sync[Sync Engine]
|
|
71
71
|
Sync --> Network[Network Layer]
|
|
72
72
|
Network --> Server[Sync Server]
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
Server --> OtherClients[Other Clients]
|
|
75
75
|
OtherClients --> Server
|
|
76
76
|
Server --> Network
|
|
@@ -95,7 +95,7 @@ Unlike traditional ORMs that hide change tracking, LiveStore makes all state cha
|
|
|
95
95
|
### Sophisticated Conflict Resolution
|
|
96
96
|
LiveStore handles conflicts intelligently using multiple strategies:
|
|
97
97
|
- **Last-Write-Wins**: Simple timestamp-based resolution
|
|
98
|
-
- **Semantic Merging**: Application-specific conflict resolution logic
|
|
98
|
+
- **Semantic Merging**: Application-specific conflict resolution logic
|
|
99
99
|
- **Operational Transforms**: For real-time collaborative text editing
|
|
100
100
|
- **CRDT Integration**: Conflict-free data types for specific use cases
|
|
101
101
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const blogSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Content management with collaborative editing capabilities\nexport const tables = {\n posts: State.SQLite.table({\n name: 'posts',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n title: State.SQLite.text(),\n content: State.SQLite.text(), // Consider using JSON for rich text operations\n slug: State.SQLite.text(),\n published: State.SQLite.boolean({ default: false }),\n authorId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n publishedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n version: State.SQLite.integer({ default: 1 }), // For optimistic concurrency\n },\n }),\n
|
|
1
|
+
export declare const blogSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Content management with collaborative editing capabilities\nexport const tables = {\n posts: State.SQLite.table({\n name: 'posts',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n title: State.SQLite.text(),\n content: State.SQLite.text(), // Consider using JSON for rich text operations\n slug: State.SQLite.text(),\n published: State.SQLite.boolean({ default: false }),\n authorId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n publishedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n version: State.SQLite.integer({ default: 1 }), // For optimistic concurrency\n },\n }),\n\n comments: State.SQLite.table({\n name: 'comments',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n postId: State.SQLite.text(),\n authorId: State.SQLite.text(),\n content: State.SQLite.text(),\n parentId: State.SQLite.text({ nullable: true }), // For threaded comments\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n editedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n\n authors: State.SQLite.table({\n name: 'authors',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n name: State.SQLite.text(),\n email: State.SQLite.text(),\n bio: State.SQLite.text({ nullable: true }),\n avatarUrl: State.SQLite.text({ nullable: true }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n // Track collaborative editing sessions\n editingSessions: State.SQLite.clientDocument({\n name: 'editingSessions',\n schema: Schema.Struct({\n postId: Schema.String,\n authorId: Schema.String,\n lastActivity: Schema.Date,\n cursorPosition: Schema.Number,\n }),\n default: {\n id: SessionIdSymbol,\n value: { postId: '', authorId: '', lastActivity: new Date(), cursorPosition: 0 }\n },\n }),\n}\n\nexport const events = {\n // Post lifecycle events\n postCreated: Events.synced({\n name: 'v1.PostCreated',\n schema: Schema.Struct({\n id: Schema.String,\n title: Schema.String,\n slug: Schema.String,\n authorId: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n postTitleChanged: Events.synced({\n name: 'v1.PostTitleChanged',\n schema: Schema.Struct({\n id: Schema.String,\n title: Schema.String,\n version: Schema.Number, // Optimistic concurrency control\n }),\n }),\n\n postContentChanged: Events.synced({\n name: 'v1.PostContentChanged',\n schema: Schema.Struct({\n id: Schema.String,\n content: Schema.String,\n version: Schema.Number,\n authorId: Schema.String,\n }),\n }),\n\n postPublished: Events.synced({\n name: 'v1.PostPublished',\n schema: Schema.Struct({\n id: Schema.String,\n publishedAt: Schema.Date,\n }),\n }),\n\n postUnpublished: Events.synced({\n name: 'v1.PostUnpublished',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n\n postDeleted: Events.synced({\n name: 'v1.PostDeleted',\n schema: Schema.Struct({\n id: Schema.String,\n deletedAt: Schema.Date,\n }),\n }),\n\n // Comment events\n commentCreated: Events.synced({\n name: 'v1.CommentCreated',\n schema: Schema.Struct({\n id: Schema.String,\n postId: Schema.String,\n authorId: Schema.String,\n content: Schema.String,\n parentId: Schema.NullOr(Schema.String),\n createdAt: Schema.Date,\n }),\n }),\n\n commentEdited: Events.synced({\n name: 'v1.CommentEdited',\n schema: Schema.Struct({\n id: Schema.String,\n content: Schema.String,\n editedAt: Schema.Date,\n }),\n }),\n\n commentDeleted: Events.synced({\n name: 'v1.CommentDeleted',\n schema: Schema.Struct({\n id: Schema.String,\n deletedAt: Schema.Date,\n }),\n }),\n\n // Author events\n authorCreated: Events.synced({\n name: 'v1.AuthorCreated',\n schema: Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n email: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n // Local editing session tracking\n editingSessionUpdated: tables.editingSessions.set,\n}\n\n// Materializers with conflict resolution strategies\nconst materializers = State.SQLite.materializers(events, {\n // Post materializers\n 'v1.PostCreated': ({ id, title, slug, authorId, createdAt }) =>\n tables.posts.insert({ id, title, content: '', slug, authorId, createdAt, version: 1 }),\n\n 'v1.PostTitleChanged': ({ id, title, version }) =>\n // Last-write-wins with version check for optimistic concurrency\n tables.posts.update({ title, version }).where({ id }),\n\n 'v1.PostContentChanged': ({ id, content, version, authorId }) =>\n tables.posts.update({ content, version }).where({ id }),\n\n 'v1.PostPublished': ({ id, publishedAt }) =>\n tables.posts.update({ published: true, publishedAt }).where({ id }),\n\n 'v1.PostUnpublished': ({ id }) =>\n tables.posts.update({ published: false, publishedAt: null }).where({ id }),\n\n 'v1.PostDeleted': ({ id, deletedAt }) =>\n tables.posts.update({ deletedAt }).where({ id }),\n\n // Comment materializers\n 'v1.CommentCreated': ({ id, postId, authorId, content, parentId, createdAt }) =>\n tables.comments.insert({ id, postId, authorId, content, parentId, createdAt }),\n\n 'v1.CommentEdited': ({ id, content, editedAt }) =>\n tables.comments.update({ content, editedAt }).where({ id }),\n\n 'v1.CommentDeleted': ({ id, deletedAt }) =>\n tables.comments.update({ deletedAt }).where({ id }),\n\n // Author materializers\n 'v1.AuthorCreated': ({ id, name, email, createdAt }) =>\n tables.authors.insert({ id, name, email, createdAt }),\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n\n// Example queries:\n//\n// // Published posts with author info\n// const publishedPosts$ = queryDb(\n// tables.posts\n// .select()\n// .join(tables.authors, 'authorId', 'id')\n// .where({ published: true, deletedAt: null })\n// .orderBy('publishedAt', 'desc'),\n// { label: 'publishedPosts' }\n// )\n//\n// // Comments for a post (threaded)\n// const postComments$ = (postId: string) => queryDb(\n// tables.comments\n// .select()\n// .join(tables.authors, 'authorId', 'id')\n// .where({ postId, deletedAt: null })\n// .orderBy('createdAt'),\n// { label: `postComments-${postId}` }\n// )";
|
|
2
2
|
//# sourceMappingURL=blog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/blog.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/blog.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,uxNA6NzB,CAAA"}
|
|
@@ -17,7 +17,7 @@ export const tables = {
|
|
|
17
17
|
version: State.SQLite.integer({ default: 1 }), // For optimistic concurrency
|
|
18
18
|
},
|
|
19
19
|
}),
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
comments: State.SQLite.table({
|
|
22
22
|
name: 'comments',
|
|
23
23
|
columns: {
|
|
@@ -31,7 +31,7 @@ export const tables = {
|
|
|
31
31
|
deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
32
32
|
},
|
|
33
33
|
}),
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
authors: State.SQLite.table({
|
|
36
36
|
name: 'authors',
|
|
37
37
|
columns: {
|
|
@@ -43,7 +43,7 @@ export const tables = {
|
|
|
43
43
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
44
44
|
},
|
|
45
45
|
}),
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// Track collaborative editing sessions
|
|
48
48
|
editingSessions: State.SQLite.clientDocument({
|
|
49
49
|
name: 'editingSessions',
|
|
@@ -53,8 +53,8 @@ export const tables = {
|
|
|
53
53
|
lastActivity: Schema.Date,
|
|
54
54
|
cursorPosition: Schema.Number,
|
|
55
55
|
}),
|
|
56
|
-
default: {
|
|
57
|
-
id: SessionIdSymbol,
|
|
56
|
+
default: {
|
|
57
|
+
id: SessionIdSymbol,
|
|
58
58
|
value: { postId: '', authorId: '', lastActivity: new Date(), cursorPosition: 0 }
|
|
59
59
|
},
|
|
60
60
|
}),
|
|
@@ -72,7 +72,7 @@ export const events = {
|
|
|
72
72
|
createdAt: Schema.Date,
|
|
73
73
|
}),
|
|
74
74
|
}),
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
postTitleChanged: Events.synced({
|
|
77
77
|
name: 'v1.PostTitleChanged',
|
|
78
78
|
schema: Schema.Struct({
|
|
@@ -81,7 +81,7 @@ export const events = {
|
|
|
81
81
|
version: Schema.Number, // Optimistic concurrency control
|
|
82
82
|
}),
|
|
83
83
|
}),
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
postContentChanged: Events.synced({
|
|
86
86
|
name: 'v1.PostContentChanged',
|
|
87
87
|
schema: Schema.Struct({
|
|
@@ -91,7 +91,7 @@ export const events = {
|
|
|
91
91
|
authorId: Schema.String,
|
|
92
92
|
}),
|
|
93
93
|
}),
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
postPublished: Events.synced({
|
|
96
96
|
name: 'v1.PostPublished',
|
|
97
97
|
schema: Schema.Struct({
|
|
@@ -99,12 +99,12 @@ export const events = {
|
|
|
99
99
|
publishedAt: Schema.Date,
|
|
100
100
|
}),
|
|
101
101
|
}),
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
postUnpublished: Events.synced({
|
|
104
104
|
name: 'v1.PostUnpublished',
|
|
105
105
|
schema: Schema.Struct({ id: Schema.String }),
|
|
106
106
|
}),
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
postDeleted: Events.synced({
|
|
109
109
|
name: 'v1.PostDeleted',
|
|
110
110
|
schema: Schema.Struct({
|
|
@@ -112,7 +112,7 @@ export const events = {
|
|
|
112
112
|
deletedAt: Schema.Date,
|
|
113
113
|
}),
|
|
114
114
|
}),
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
// Comment events
|
|
117
117
|
commentCreated: Events.synced({
|
|
118
118
|
name: 'v1.CommentCreated',
|
|
@@ -125,7 +125,7 @@ export const events = {
|
|
|
125
125
|
createdAt: Schema.Date,
|
|
126
126
|
}),
|
|
127
127
|
}),
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
commentEdited: Events.synced({
|
|
130
130
|
name: 'v1.CommentEdited',
|
|
131
131
|
schema: Schema.Struct({
|
|
@@ -134,7 +134,7 @@ export const events = {
|
|
|
134
134
|
editedAt: Schema.Date,
|
|
135
135
|
}),
|
|
136
136
|
}),
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
commentDeleted: Events.synced({
|
|
139
139
|
name: 'v1.CommentDeleted',
|
|
140
140
|
schema: Schema.Struct({
|
|
@@ -142,7 +142,7 @@ export const events = {
|
|
|
142
142
|
deletedAt: Schema.Date,
|
|
143
143
|
}),
|
|
144
144
|
}),
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
// Author events
|
|
147
147
|
authorCreated: Events.synced({
|
|
148
148
|
name: 'v1.AuthorCreated',
|
|
@@ -153,7 +153,7 @@ export const events = {
|
|
|
153
153
|
createdAt: Schema.Date,
|
|
154
154
|
}),
|
|
155
155
|
}),
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
// Local editing session tracking
|
|
158
158
|
editingSessionUpdated: tables.editingSessions.set,
|
|
159
159
|
}
|
|
@@ -163,33 +163,33 @@ const materializers = State.SQLite.materializers(events, {
|
|
|
163
163
|
// Post materializers
|
|
164
164
|
'v1.PostCreated': ({ id, title, slug, authorId, createdAt }) =>
|
|
165
165
|
tables.posts.insert({ id, title, content: '', slug, authorId, createdAt, version: 1 }),
|
|
166
|
-
|
|
166
|
+
|
|
167
167
|
'v1.PostTitleChanged': ({ id, title, version }) =>
|
|
168
168
|
// Last-write-wins with version check for optimistic concurrency
|
|
169
169
|
tables.posts.update({ title, version }).where({ id }),
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
'v1.PostContentChanged': ({ id, content, version, authorId }) =>
|
|
172
172
|
tables.posts.update({ content, version }).where({ id }),
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
'v1.PostPublished': ({ id, publishedAt }) =>
|
|
175
175
|
tables.posts.update({ published: true, publishedAt }).where({ id }),
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
'v1.PostUnpublished': ({ id }) =>
|
|
178
178
|
tables.posts.update({ published: false, publishedAt: null }).where({ id }),
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
'v1.PostDeleted': ({ id, deletedAt }) =>
|
|
181
181
|
tables.posts.update({ deletedAt }).where({ id }),
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
// Comment materializers
|
|
184
184
|
'v1.CommentCreated': ({ id, postId, authorId, content, parentId, createdAt }) =>
|
|
185
185
|
tables.comments.insert({ id, postId, authorId, content, parentId, createdAt }),
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
'v1.CommentEdited': ({ id, content, editedAt }) =>
|
|
188
188
|
tables.comments.update({ content, editedAt }).where({ id }),
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
'v1.CommentDeleted': ({ id, deletedAt }) =>
|
|
191
191
|
tables.comments.update({ deletedAt }).where({ id }),
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
// Author materializers
|
|
194
194
|
'v1.AuthorCreated': ({ id, name, email, createdAt }) =>
|
|
195
195
|
tables.authors.insert({ id, name, email, createdAt }),
|
|
@@ -215,7 +215,7 @@ export const schema = makeSchema({ events, state })
|
|
|
215
215
|
// const postComments$ = (postId: string) => queryDb(
|
|
216
216
|
// tables.comments
|
|
217
217
|
// .select()
|
|
218
|
-
// .join(tables.authors, 'authorId', 'id')
|
|
218
|
+
// .join(tables.authors, 'authorId', 'id')
|
|
219
219
|
// .where({ postId, deletedAt: null })
|
|
220
220
|
// .orderBy('createdAt'),
|
|
221
221
|
// { label: \`postComments-\${postId}\` }
|