@livestore/cli 0.4.0-dev.9 → 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.
Files changed (136) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/__tests__/fixtures/mock-config.d.ts +63 -0
  3. package/dist/__tests__/fixtures/mock-config.d.ts.map +1 -0
  4. package/dist/__tests__/fixtures/mock-config.js +94 -0
  5. package/dist/__tests__/fixtures/mock-config.js.map +1 -0
  6. package/dist/__tests__/sync-operations.test.d.ts +2 -0
  7. package/dist/__tests__/sync-operations.test.d.ts.map +1 -0
  8. package/dist/__tests__/sync-operations.test.js +167 -0
  9. package/dist/__tests__/sync-operations.test.js.map +1 -0
  10. package/dist/bin.js +6 -5
  11. package/dist/bin.js.map +1 -1
  12. package/dist/cli.d.ts +2 -9
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +4 -5
  15. package/dist/cli.js.map +1 -1
  16. package/dist/commands/__fixtures__/mcp-protocol-server.d.ts +2 -0
  17. package/dist/commands/__fixtures__/mcp-protocol-server.d.ts.map +1 -0
  18. package/dist/commands/__fixtures__/mcp-protocol-server.js +10 -0
  19. package/dist/commands/__fixtures__/mcp-protocol-server.js.map +1 -0
  20. package/dist/commands/import-export.d.ts +20 -0
  21. package/dist/commands/import-export.d.ts.map +1 -0
  22. package/dist/commands/import-export.js +135 -0
  23. package/dist/commands/import-export.js.map +1 -0
  24. package/dist/commands/mcp-coach.d.ts +21 -12
  25. package/dist/commands/mcp-coach.d.ts.map +1 -1
  26. package/dist/commands/mcp-coach.js +49 -53
  27. package/dist/commands/mcp-coach.js.map +1 -1
  28. package/dist/commands/mcp-protocols.d.ts +7 -0
  29. package/dist/commands/mcp-protocols.d.ts.map +1 -0
  30. package/dist/commands/mcp-protocols.js +12 -0
  31. package/dist/commands/mcp-protocols.js.map +1 -0
  32. package/dist/commands/mcp-protocols.test.d.ts +2 -0
  33. package/dist/commands/mcp-protocols.test.d.ts.map +1 -0
  34. package/dist/commands/mcp-protocols.test.js +60 -0
  35. package/dist/commands/mcp-protocols.test.js.map +1 -0
  36. package/dist/commands/mcp-stdio.test.d.ts +2 -0
  37. package/dist/commands/mcp-stdio.test.d.ts.map +1 -0
  38. package/dist/commands/mcp-stdio.test.js +79 -0
  39. package/dist/commands/mcp-stdio.test.js.map +1 -0
  40. package/dist/commands/mcp-tool-handlers.d.ts +6 -0
  41. package/dist/commands/mcp-tool-handlers.d.ts.map +1 -0
  42. package/dist/commands/{mcp-tools.js → mcp-tool-handlers.js} +80 -45
  43. package/dist/commands/mcp-tool-handlers.js.map +1 -0
  44. package/dist/commands/mcp-tools-defs.d.ts +134 -0
  45. package/dist/commands/mcp-tools-defs.d.ts.map +1 -0
  46. package/dist/commands/mcp-tools-defs.js +285 -0
  47. package/dist/commands/mcp-tools-defs.js.map +1 -0
  48. package/dist/commands/mcp-tools-defs.test.d.ts +2 -0
  49. package/dist/commands/mcp-tools-defs.test.d.ts.map +1 -0
  50. package/dist/commands/mcp-tools-defs.test.js +11 -0
  51. package/dist/commands/mcp-tools-defs.test.js.map +1 -0
  52. package/dist/commands/mcp.d.ts +1 -3
  53. package/dist/commands/mcp.d.ts.map +1 -1
  54. package/dist/commands/mcp.js +7 -5
  55. package/dist/commands/mcp.js.map +1 -1
  56. package/dist/commands/new-project.d.ts +22 -23
  57. package/dist/commands/new-project.d.ts.map +1 -1
  58. package/dist/commands/new-project.js +85 -44
  59. package/dist/commands/new-project.js.map +1 -1
  60. package/dist/mcp-content/architecture.d.ts +1 -1
  61. package/dist/mcp-content/architecture.d.ts.map +1 -1
  62. package/dist/mcp-content/architecture.js +3 -3
  63. package/dist/mcp-content/features.d.ts +1 -1
  64. package/dist/mcp-content/features.d.ts.map +1 -1
  65. package/dist/mcp-content/features.js +1 -1
  66. package/dist/mcp-content/getting-started.d.ts +1 -1
  67. package/dist/mcp-content/getting-started.d.ts.map +1 -1
  68. package/dist/mcp-content/getting-started.js +37 -37
  69. package/dist/mcp-content/overview.d.ts +1 -1
  70. package/dist/mcp-content/overview.d.ts.map +1 -1
  71. package/dist/mcp-content/overview.js +6 -6
  72. package/dist/mcp-content/schemas/blog.d.ts +1 -1
  73. package/dist/mcp-content/schemas/blog.d.ts.map +1 -1
  74. package/dist/mcp-content/schemas/blog.js +25 -25
  75. package/dist/mcp-content/schemas/ecommerce.d.ts +1 -1
  76. package/dist/mcp-content/schemas/ecommerce.d.ts.map +1 -1
  77. package/dist/mcp-content/schemas/ecommerce.js +67 -67
  78. package/dist/mcp-content/schemas/social.d.ts +1 -1
  79. package/dist/mcp-content/schemas/social.d.ts.map +1 -1
  80. package/dist/mcp-content/schemas/social.js +48 -48
  81. package/dist/mcp-content/schemas/todo.d.ts +1 -1
  82. package/dist/mcp-content/schemas/todo.d.ts.map +1 -1
  83. package/dist/mcp-content/schemas/todo.js +35 -35
  84. package/dist/mcp-runtime/runtime.d.ts +48 -0
  85. package/dist/mcp-runtime/runtime.d.ts.map +1 -0
  86. package/dist/mcp-runtime/runtime.js +107 -0
  87. package/dist/mcp-runtime/runtime.js.map +1 -0
  88. package/dist/module-loader.d.ts +22 -0
  89. package/dist/module-loader.d.ts.map +1 -0
  90. package/dist/module-loader.js +75 -0
  91. package/dist/module-loader.js.map +1 -0
  92. package/dist/package-manager.d.ts +38 -0
  93. package/dist/package-manager.d.ts.map +1 -0
  94. package/dist/package-manager.js +33 -0
  95. package/dist/package-manager.js.map +1 -0
  96. package/dist/package-manager.test.d.ts +2 -0
  97. package/dist/package-manager.test.d.ts.map +1 -0
  98. package/dist/package-manager.test.js +57 -0
  99. package/dist/package-manager.test.js.map +1 -0
  100. package/dist/sync-operations.d.ts +115 -0
  101. package/dist/sync-operations.d.ts.map +1 -0
  102. package/dist/sync-operations.js +178 -0
  103. package/dist/sync-operations.js.map +1 -0
  104. package/package.json +57 -15
  105. package/src/__tests__/fixtures/mock-config.ts +112 -0
  106. package/src/__tests__/sync-operations.test.ts +218 -0
  107. package/src/bin.ts +8 -6
  108. package/src/cli.ts +5 -6
  109. package/src/commands/__fixtures__/mcp-protocol-server.ts +12 -0
  110. package/src/commands/import-export.ts +283 -0
  111. package/src/commands/mcp-coach.ts +76 -75
  112. package/src/commands/mcp-protocols.test.ts +76 -0
  113. package/src/commands/mcp-protocols.ts +12 -0
  114. package/src/commands/mcp-stdio.test.ts +93 -0
  115. package/src/commands/{mcp-tools.ts → mcp-tool-handlers.ts} +100 -61
  116. package/src/commands/mcp-tools-defs.test.ts +13 -0
  117. package/src/commands/mcp-tools-defs.ts +320 -0
  118. package/src/commands/mcp.ts +10 -6
  119. package/src/commands/new-project.ts +124 -57
  120. package/src/mcp-content/architecture.ts +3 -3
  121. package/src/mcp-content/features.ts +1 -1
  122. package/src/mcp-content/getting-started.ts +37 -37
  123. package/src/mcp-content/overview.ts +6 -6
  124. package/src/mcp-content/schemas/blog.ts +25 -25
  125. package/src/mcp-content/schemas/ecommerce.ts +67 -67
  126. package/src/mcp-content/schemas/social.ts +48 -48
  127. package/src/mcp-content/schemas/todo.ts +35 -35
  128. package/src/mcp-runtime/runtime.ts +147 -0
  129. package/src/module-loader.ts +94 -0
  130. package/src/package-manager.test.ts +66 -0
  131. package/src/package-manager.ts +44 -0
  132. package/src/sync-operations.ts +360 -0
  133. package/dist/commands/mcp-tools.d.ts +0 -58
  134. package/dist/commands/mcp-tools.d.ts.map +0 -1
  135. package/dist/commands/mcp-tools.js.map +0 -1
  136. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -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 \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).";
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,y4LAsHuI,CAAA"}
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 \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// )";
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,w1NA6NzB,CAAA"}
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}\` }
@@ -1,2 +1,2 @@
1
- export declare const ecommerceSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// E-commerce with inventory management and order processing\nexport const tables = {\n products: State.SQLite.table({\n name: 'products',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n sku: State.SQLite.text(),\n name: State.SQLite.text(),\n description: State.SQLite.text(),\n price: State.SQLite.integer(), // Store as cents to avoid floating point issues\n currency: State.SQLite.text({ default: 'USD' }),\n stock: State.SQLite.integer({ default: 0 }),\n reservedStock: State.SQLite.integer({ default: 0 }), // For pending orders\n isActive: State.SQLite.boolean({ default: true }),\n weight: State.SQLite.real({ nullable: true }), // For shipping calculations\n dimensions: State.SQLite.text({ nullable: true }), // JSON: {width, height, depth}\n imageUrls: State.SQLite.text({ nullable: true }), // JSON array\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n updatedAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n \n categories: State.SQLite.table({\n name: 'categories',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n name: State.SQLite.text(),\n slug: State.SQLite.text(),\n parentId: State.SQLite.text({ nullable: true }), // For hierarchical categories\n description: State.SQLite.text({ nullable: true }),\n imageUrl: State.SQLite.text({ nullable: true }),\n sortOrder: State.SQLite.integer({ default: 0 }),\n isActive: State.SQLite.boolean({ default: true }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n productCategories: State.SQLite.table({\n name: 'product_categories',\n columns: {\n productId: State.SQLite.text(),\n categoryId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n customers: State.SQLite.table({\n name: 'customers',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n email: State.SQLite.text(),\n firstName: State.SQLite.text({ nullable: true }),\n lastName: State.SQLite.text({ nullable: true }),\n phone: State.SQLite.text({ nullable: true }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n lastOrderAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n \n addresses: State.SQLite.table({\n name: 'addresses',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n customerId: State.SQLite.text(),\n type: State.SQLite.text({ default: 'shipping' }), // shipping, billing\n firstName: State.SQLite.text(),\n lastName: State.SQLite.text(),\n company: State.SQLite.text({ nullable: true }),\n address1: State.SQLite.text(),\n address2: State.SQLite.text({ nullable: true }),\n city: State.SQLite.text(),\n state: State.SQLite.text(),\n country: State.SQLite.text(),\n postalCode: State.SQLite.text(),\n isDefault: State.SQLite.boolean({ default: false }),\n },\n }),\n \n orders: State.SQLite.table({\n name: 'orders',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n orderNumber: State.SQLite.text(), // Human-readable order number\n customerId: State.SQLite.text(),\n status: State.SQLite.text({ default: 'draft' }), // draft, pending, paid, processing, shipped, delivered, cancelled\n paymentStatus: State.SQLite.text({ default: 'pending' }), // pending, paid, failed, refunded\n fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }), // unfulfilled, partial, fulfilled\n \n subtotal: State.SQLite.integer(), // In cents\n taxAmount: State.SQLite.integer({ default: 0 }),\n shippingAmount: State.SQLite.integer({ default: 0 }),\n discountAmount: State.SQLite.integer({ default: 0 }),\n total: State.SQLite.integer(),\n currency: State.SQLite.text({ default: 'USD' }),\n \n shippingAddress: State.SQLite.text(), // JSON\n billingAddress: State.SQLite.text(), // JSON\n \n notes: State.SQLite.text({ nullable: true }),\n \n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n updatedAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n cancelledAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n shippedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n deliveredAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n \n orderItems: State.SQLite.table({\n name: 'order_items',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n orderId: State.SQLite.text(),\n productId: State.SQLite.text(),\n variantId: State.SQLite.text({ nullable: true }),\n sku: State.SQLite.text(), // Snapshot at time of order\n name: State.SQLite.text(), // Product name at time of order\n quantity: State.SQLite.integer(),\n unitPrice: State.SQLite.integer(), // Price per unit in cents\n totalPrice: State.SQLite.integer(), // quantity * unitPrice\n fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }),\n },\n }),\n \n // Inventory tracking with event sourcing\n inventoryEvents: State.SQLite.table({\n name: 'inventory_events',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n productId: State.SQLite.text(),\n type: State.SQLite.text(), // 'adjustment', 'sale', 'return', 'damage', 'restock'\n quantity: State.SQLite.integer(), // Can be positive or negative\n reason: State.SQLite.text({ nullable: true }),\n referenceId: State.SQLite.text({ nullable: true }), // Order ID, adjustment ID, etc.\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n // Shopping cart (client-side state)\n cart: State.SQLite.clientDocument({\n name: 'cart',\n schema: Schema.Struct({\n items: Schema.Array(Schema.Struct({\n productId: Schema.String,\n quantity: Schema.Number,\n addedAt: Schema.Date,\n })),\n discountCode: Schema.NullOr(Schema.String),\n notes: Schema.String,\n }),\n default: { \n id: SessionIdSymbol, \n value: { items: [], discountCode: null, notes: '' }\n },\n }),\n}\n\nexport const events = {\n // Product management\n productCreated: Events.synced({\n name: 'v1.ProductCreated',\n schema: Schema.Struct({\n id: Schema.String,\n sku: Schema.String,\n name: Schema.String,\n description: Schema.String,\n price: Schema.Number, // In cents\n currency: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n \n productUpdated: Events.synced({\n name: 'v1.ProductUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n name: Schema.NullOr(Schema.String),\n description: Schema.NullOr(Schema.String),\n price: Schema.NullOr(Schema.Number),\n updatedAt: Schema.Date,\n }),\n }),\n \n productStockAdjusted: Events.synced({\n name: 'v1.ProductStockAdjusted',\n schema: Schema.Struct({\n productId: Schema.String,\n adjustment: Schema.Number, // Can be positive or negative\n reason: Schema.String,\n referenceId: Schema.NullOr(Schema.String),\n createdAt: Schema.Date,\n }),\n }),\n \n productDeactivated: Events.synced({\n name: 'v1.ProductDeactivated',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n \n // Customer management\n customerCreated: Events.synced({\n name: 'v1.CustomerCreated',\n schema: Schema.Struct({\n id: Schema.String,\n email: Schema.String,\n firstName: Schema.NullOr(Schema.String),\n lastName: Schema.NullOr(Schema.String),\n createdAt: Schema.Date,\n }),\n }),\n \n // Order lifecycle with state machine\n orderCreated: Events.synced({\n name: 'v1.OrderCreated',\n schema: Schema.Struct({\n id: Schema.String,\n orderNumber: Schema.String,\n customerId: Schema.String,\n items: Schema.Array(Schema.Struct({\n productId: Schema.String,\n sku: Schema.String,\n name: Schema.String,\n quantity: Schema.Number,\n unitPrice: Schema.Number,\n })),\n subtotal: Schema.Number,\n total: Schema.Number,\n shippingAddress: Schema.Object,\n billingAddress: Schema.Object,\n createdAt: Schema.Date,\n }),\n }),\n \n orderPaymentReceived: Events.synced({\n name: 'v1.OrderPaymentReceived',\n schema: Schema.Struct({\n orderId: Schema.String,\n amount: Schema.Number,\n paymentMethod: Schema.String,\n transactionId: Schema.String,\n paidAt: Schema.Date,\n }),\n }),\n \n orderShipped: Events.synced({\n name: 'v1.OrderShipped',\n schema: Schema.Struct({\n orderId: Schema.String,\n trackingNumber: Schema.NullOr(Schema.String),\n carrier: Schema.NullOr(Schema.String),\n shippedAt: Schema.Date,\n }),\n }),\n \n orderDelivered: Events.synced({\n name: 'v1.OrderDelivered',\n schema: Schema.Struct({\n orderId: Schema.String,\n deliveredAt: Schema.Date,\n }),\n }),\n \n orderCancelled: Events.synced({\n name: 'v1.OrderCancelled',\n schema: Schema.Struct({\n orderId: Schema.String,\n reason: Schema.String,\n cancelledAt: Schema.Date,\n }),\n }),\n \n // Cart management (local)\n cartUpdated: tables.cart.set,\n}\n\n// Materializers with business logic and constraints\nconst materializers = State.SQLite.materializers(events, {\n // Product materializers\n 'v1.ProductCreated': ({ id, sku, name, description, price, currency, createdAt }) =>\n tables.products.insert({ id, sku, name, description, price, currency, createdAt, updatedAt: createdAt }),\n \n 'v1.ProductUpdated': ({ id, name, description, price, updatedAt }) =>\n tables.products.update({ \n name: name ?? undefined,\n description: description ?? undefined, \n price: price ?? undefined,\n updatedAt \n }).where({ id }),\n \n 'v1.ProductStockAdjusted': ({ productId, adjustment, reason, referenceId, createdAt }) => [\n // Record the inventory event\n tables.inventoryEvents.insert({ \n id: crypto.randomUUID(),\n productId, \n type: 'adjustment',\n quantity: adjustment, \n reason, \n referenceId, \n createdAt \n }),\n // Update product stock (eventually consistent)\n tables.products.update({ \n stock: Math.max(0, tables.products.select('stock').where({ id: productId }).scalar() + adjustment),\n updatedAt: createdAt\n }).where({ id: productId }),\n ],\n \n 'v1.ProductDeactivated': ({ id }) =>\n tables.products.update({ isActive: false }).where({ id }),\n \n // Customer materializers\n 'v1.CustomerCreated': ({ id, email, firstName, lastName, createdAt }) =>\n tables.customers.insert({ id, email, firstName, lastName, createdAt }),\n \n // Order materializers with inventory reservation\n 'v1.OrderCreated': ({ id, orderNumber, customerId, items, subtotal, total, shippingAddress, billingAddress, createdAt }) => [\n // Create the order\n tables.orders.insert({ \n id, \n orderNumber, \n customerId, \n status: 'pending',\n subtotal, \n total, \n shippingAddress: JSON.stringify(shippingAddress),\n billingAddress: JSON.stringify(billingAddress),\n createdAt, \n updatedAt: createdAt \n }),\n // Create order items and reserve inventory\n ...items.flatMap(item => [\n tables.orderItems.insert({ \n id: crypto.randomUUID(),\n orderId: id,\n productId: item.productId,\n sku: item.sku,\n name: item.name,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.quantity * item.unitPrice,\n }),\n // Reserve stock\n tables.products.update({ \n reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() + item.quantity\n }).where({ id: item.productId }),\n ]),\n ],\n \n 'v1.OrderPaymentReceived': ({ orderId, amount, paymentMethod, transactionId, paidAt }) =>\n tables.orders.update({ \n status: 'paid', \n paymentStatus: 'paid',\n updatedAt: paidAt \n }).where({ id: orderId }),\n \n 'v1.OrderShipped': ({ orderId, trackingNumber, carrier, shippedAt }) => [\n tables.orders.update({ \n status: 'shipped',\n fulfillmentStatus: 'fulfilled',\n shippedAt,\n updatedAt: shippedAt \n }).where({ id: orderId }),\n // Convert reserved stock to actual stock reduction\n ...tables.orderItems.select().where({ orderId }).map(item => \n tables.products.update({ \n stock: tables.products.select('stock').where({ id: item.productId }).scalar() - item.quantity,\n reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity\n }).where({ id: item.productId })\n ),\n ],\n \n 'v1.OrderDelivered': ({ orderId, deliveredAt }) =>\n tables.orders.update({ \n status: 'delivered',\n deliveredAt,\n updatedAt: deliveredAt \n }).where({ id: orderId }),\n \n 'v1.OrderCancelled': ({ orderId, reason, cancelledAt }) => [\n tables.orders.update({ \n status: 'cancelled',\n cancelledAt,\n notes: reason,\n updatedAt: cancelledAt \n }).where({ id: orderId }),\n // Release reserved inventory\n ...tables.orderItems.select().where({ orderId }).map(item => \n tables.products.update({ \n reservedStock: Math.max(0, tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity)\n }).where({ id: item.productId })\n ),\n ],\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n\n// Example queries for business intelligence:\n//\n// // Available products with real-time stock\n// const availableProducts$ = queryDb(\n// tables.products\n// .select()\n// .where({ isActive: true, deletedAt: null })\n// .having(tables.products.column('stock').minus(tables.products.column('reservedStock')).gt(0))\n// .orderBy('name'),\n// { label: 'availableProducts' }\n// )\n//\n// // Orders requiring fulfillment\n// const pendingOrders$ = queryDb(\n// tables.orders\n// .select()\n// .join(tables.customers, 'customerId', 'id')\n// .where({ \n// 'orders.status': 'paid',\n// 'orders.fulfillmentStatus': 'unfulfilled'\n// })\n// .orderBy('orders.createdAt'),\n// { label: 'pendingOrders' }\n// )\n//\n// // Low stock alerts\n// const lowStockProducts$ = queryDb(\n// tables.products\n// .select()\n// .where({ isActive: true })\n// .having(tables.products.column('stock').minus(tables.products.column('reservedStock')).lt(10))\n// .orderBy('stock'),\n// { label: 'lowStockProducts' }\n// )";
1
+ export declare const ecommerceSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// E-commerce with inventory management and order processing\nexport const tables = {\n products: State.SQLite.table({\n name: 'products',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n sku: State.SQLite.text(),\n name: State.SQLite.text(),\n description: State.SQLite.text(),\n price: State.SQLite.integer(), // Store as cents to avoid floating point issues\n currency: State.SQLite.text({ default: 'USD' }),\n stock: State.SQLite.integer({ default: 0 }),\n reservedStock: State.SQLite.integer({ default: 0 }), // For pending orders\n isActive: State.SQLite.boolean({ default: true }),\n weight: State.SQLite.real({ nullable: true }), // For shipping calculations\n dimensions: State.SQLite.text({ nullable: true }), // JSON: {width, height, depth}\n imageUrls: State.SQLite.text({ nullable: true }), // JSON array\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n updatedAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n\n categories: State.SQLite.table({\n name: 'categories',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n name: State.SQLite.text(),\n slug: State.SQLite.text(),\n parentId: State.SQLite.text({ nullable: true }), // For hierarchical categories\n description: State.SQLite.text({ nullable: true }),\n imageUrl: State.SQLite.text({ nullable: true }),\n sortOrder: State.SQLite.integer({ default: 0 }),\n isActive: State.SQLite.boolean({ default: true }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n productCategories: State.SQLite.table({\n name: 'product_categories',\n columns: {\n productId: State.SQLite.text(),\n categoryId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n customers: State.SQLite.table({\n name: 'customers',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n email: State.SQLite.text(),\n firstName: State.SQLite.text({ nullable: true }),\n lastName: State.SQLite.text({ nullable: true }),\n phone: State.SQLite.text({ nullable: true }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n lastOrderAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n\n addresses: State.SQLite.table({\n name: 'addresses',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n customerId: State.SQLite.text(),\n type: State.SQLite.text({ default: 'shipping' }), // shipping, billing\n firstName: State.SQLite.text(),\n lastName: State.SQLite.text(),\n company: State.SQLite.text({ nullable: true }),\n address1: State.SQLite.text(),\n address2: State.SQLite.text({ nullable: true }),\n city: State.SQLite.text(),\n state: State.SQLite.text(),\n country: State.SQLite.text(),\n postalCode: State.SQLite.text(),\n isDefault: State.SQLite.boolean({ default: false }),\n },\n }),\n\n orders: State.SQLite.table({\n name: 'orders',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n orderNumber: State.SQLite.text(), // Human-readable order number\n customerId: State.SQLite.text(),\n status: State.SQLite.text({ default: 'draft' }), // draft, pending, paid, processing, shipped, delivered, cancelled\n paymentStatus: State.SQLite.text({ default: 'pending' }), // pending, paid, failed, refunded\n fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }), // unfulfilled, partial, fulfilled\n\n subtotal: State.SQLite.integer(), // In cents\n taxAmount: State.SQLite.integer({ default: 0 }),\n shippingAmount: State.SQLite.integer({ default: 0 }),\n discountAmount: State.SQLite.integer({ default: 0 }),\n total: State.SQLite.integer(),\n currency: State.SQLite.text({ default: 'USD' }),\n\n shippingAddress: State.SQLite.text(), // JSON\n billingAddress: State.SQLite.text(), // JSON\n\n notes: State.SQLite.text({ nullable: true }),\n\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n updatedAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n cancelledAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n shippedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n deliveredAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n\n orderItems: State.SQLite.table({\n name: 'order_items',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n orderId: State.SQLite.text(),\n productId: State.SQLite.text(),\n variantId: State.SQLite.text({ nullable: true }),\n sku: State.SQLite.text(), // Snapshot at time of order\n name: State.SQLite.text(), // Product name at time of order\n quantity: State.SQLite.integer(),\n unitPrice: State.SQLite.integer(), // Price per unit in cents\n totalPrice: State.SQLite.integer(), // quantity * unitPrice\n fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }),\n },\n }),\n\n // Inventory tracking with event sourcing\n inventoryEvents: State.SQLite.table({\n name: 'inventory_events',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n productId: State.SQLite.text(),\n type: State.SQLite.text(), // 'adjustment', 'sale', 'return', 'damage', 'restock'\n quantity: State.SQLite.integer(), // Can be positive or negative\n reason: State.SQLite.text({ nullable: true }),\n referenceId: State.SQLite.text({ nullable: true }), // Order ID, adjustment ID, etc.\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n // Shopping cart (client-side state)\n cart: State.SQLite.clientDocument({\n name: 'cart',\n schema: Schema.Struct({\n items: Schema.Array(Schema.Struct({\n productId: Schema.String,\n quantity: Schema.Number,\n addedAt: Schema.Date,\n })),\n discountCode: Schema.NullOr(Schema.String),\n notes: Schema.String,\n }),\n default: {\n id: SessionIdSymbol,\n value: { items: [], discountCode: null, notes: '' }\n },\n }),\n}\n\nexport const events = {\n // Product management\n productCreated: Events.synced({\n name: 'v1.ProductCreated',\n schema: Schema.Struct({\n id: Schema.String,\n sku: Schema.String,\n name: Schema.String,\n description: Schema.String,\n price: Schema.Number, // In cents\n currency: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n productUpdated: Events.synced({\n name: 'v1.ProductUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n name: Schema.NullOr(Schema.String),\n description: Schema.NullOr(Schema.String),\n price: Schema.NullOr(Schema.Number),\n updatedAt: Schema.Date,\n }),\n }),\n\n productStockAdjusted: Events.synced({\n name: 'v1.ProductStockAdjusted',\n schema: Schema.Struct({\n productId: Schema.String,\n adjustment: Schema.Number, // Can be positive or negative\n reason: Schema.String,\n referenceId: Schema.NullOr(Schema.String),\n createdAt: Schema.Date,\n }),\n }),\n\n productDeactivated: Events.synced({\n name: 'v1.ProductDeactivated',\n schema: Schema.Struct({ id: Schema.String }),\n }),\n\n // Customer management\n customerCreated: Events.synced({\n name: 'v1.CustomerCreated',\n schema: Schema.Struct({\n id: Schema.String,\n email: Schema.String,\n firstName: Schema.NullOr(Schema.String),\n lastName: Schema.NullOr(Schema.String),\n createdAt: Schema.Date,\n }),\n }),\n\n // Order lifecycle with state machine\n orderCreated: Events.synced({\n name: 'v1.OrderCreated',\n schema: Schema.Struct({\n id: Schema.String,\n orderNumber: Schema.String,\n customerId: Schema.String,\n items: Schema.Array(Schema.Struct({\n productId: Schema.String,\n sku: Schema.String,\n name: Schema.String,\n quantity: Schema.Number,\n unitPrice: Schema.Number,\n })),\n subtotal: Schema.Number,\n total: Schema.Number,\n shippingAddress: Schema.Object,\n billingAddress: Schema.Object,\n createdAt: Schema.Date,\n }),\n }),\n\n orderPaymentReceived: Events.synced({\n name: 'v1.OrderPaymentReceived',\n schema: Schema.Struct({\n orderId: Schema.String,\n amount: Schema.Number,\n paymentMethod: Schema.String,\n transactionId: Schema.String,\n paidAt: Schema.Date,\n }),\n }),\n\n orderShipped: Events.synced({\n name: 'v1.OrderShipped',\n schema: Schema.Struct({\n orderId: Schema.String,\n trackingNumber: Schema.NullOr(Schema.String),\n carrier: Schema.NullOr(Schema.String),\n shippedAt: Schema.Date,\n }),\n }),\n\n orderDelivered: Events.synced({\n name: 'v1.OrderDelivered',\n schema: Schema.Struct({\n orderId: Schema.String,\n deliveredAt: Schema.Date,\n }),\n }),\n\n orderCancelled: Events.synced({\n name: 'v1.OrderCancelled',\n schema: Schema.Struct({\n orderId: Schema.String,\n reason: Schema.String,\n cancelledAt: Schema.Date,\n }),\n }),\n\n // Cart management (local)\n cartUpdated: tables.cart.set,\n}\n\n// Materializers with business logic and constraints\nconst materializers = State.SQLite.materializers(events, {\n // Product materializers\n 'v1.ProductCreated': ({ id, sku, name, description, price, currency, createdAt }) =>\n tables.products.insert({ id, sku, name, description, price, currency, createdAt, updatedAt: createdAt }),\n\n 'v1.ProductUpdated': ({ id, name, description, price, updatedAt }) =>\n tables.products.update({\n name: name ?? undefined,\n description: description ?? undefined,\n price: price ?? undefined,\n updatedAt\n }).where({ id }),\n\n 'v1.ProductStockAdjusted': ({ productId, adjustment, reason, referenceId, createdAt }) => [\n // Record the inventory event\n tables.inventoryEvents.insert({\n id: crypto.randomUUID(),\n productId,\n type: 'adjustment',\n quantity: adjustment,\n reason,\n referenceId,\n createdAt\n }),\n // Update product stock (eventually consistent)\n tables.products.update({\n stock: Math.max(0, tables.products.select('stock').where({ id: productId }).scalar() + adjustment),\n updatedAt: createdAt\n }).where({ id: productId }),\n ],\n\n 'v1.ProductDeactivated': ({ id }) =>\n tables.products.update({ isActive: false }).where({ id }),\n\n // Customer materializers\n 'v1.CustomerCreated': ({ id, email, firstName, lastName, createdAt }) =>\n tables.customers.insert({ id, email, firstName, lastName, createdAt }),\n\n // Order materializers with inventory reservation\n 'v1.OrderCreated': ({ id, orderNumber, customerId, items, subtotal, total, shippingAddress, billingAddress, createdAt }) => [\n // Create the order\n tables.orders.insert({\n id,\n orderNumber,\n customerId,\n status: 'pending',\n subtotal,\n total,\n shippingAddress: JSON.stringify(shippingAddress),\n billingAddress: JSON.stringify(billingAddress),\n createdAt,\n updatedAt: createdAt\n }),\n // Create order items and reserve inventory\n ...items.flatMap(item => [\n tables.orderItems.insert({\n id: crypto.randomUUID(),\n orderId: id,\n productId: item.productId,\n sku: item.sku,\n name: item.name,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.quantity * item.unitPrice,\n }),\n // Reserve stock\n tables.products.update({\n reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() + item.quantity\n }).where({ id: item.productId }),\n ]),\n ],\n\n 'v1.OrderPaymentReceived': ({ orderId, amount, paymentMethod, transactionId, paidAt }) =>\n tables.orders.update({\n status: 'paid',\n paymentStatus: 'paid',\n updatedAt: paidAt\n }).where({ id: orderId }),\n\n 'v1.OrderShipped': ({ orderId, trackingNumber, carrier, shippedAt }) => [\n tables.orders.update({\n status: 'shipped',\n fulfillmentStatus: 'fulfilled',\n shippedAt,\n updatedAt: shippedAt\n }).where({ id: orderId }),\n // Convert reserved stock to actual stock reduction\n ...tables.orderItems.select().where({ orderId }).map(item =>\n tables.products.update({\n stock: tables.products.select('stock').where({ id: item.productId }).scalar() - item.quantity,\n reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity\n }).where({ id: item.productId })\n ),\n ],\n\n 'v1.OrderDelivered': ({ orderId, deliveredAt }) =>\n tables.orders.update({\n status: 'delivered',\n deliveredAt,\n updatedAt: deliveredAt\n }).where({ id: orderId }),\n\n 'v1.OrderCancelled': ({ orderId, reason, cancelledAt }) => [\n tables.orders.update({\n status: 'cancelled',\n cancelledAt,\n notes: reason,\n updatedAt: cancelledAt\n }).where({ id: orderId }),\n // Release reserved inventory\n ...tables.orderItems.select().where({ orderId }).map(item =>\n tables.products.update({\n reservedStock: Math.max(0, tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity)\n }).where({ id: item.productId })\n ),\n ],\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n\n// Example queries for business intelligence:\n//\n// // Available products with real-time stock\n// const availableProducts$ = queryDb(\n// tables.products\n// .select()\n// .where({ isActive: true, deletedAt: null })\n// .having(tables.products.column('stock').minus(tables.products.column('reservedStock')).gt(0))\n// .orderBy('name'),\n// { label: 'availableProducts' }\n// )\n//\n// // Orders requiring fulfillment\n// const pendingOrders$ = queryDb(\n// tables.orders\n// .select()\n// .join(tables.customers, 'customerId', 'id')\n// .where({\n// 'orders.status': 'paid',\n// 'orders.fulfillmentStatus': 'unfulfilled'\n// })\n// .orderBy('orders.createdAt'),\n// { label: 'pendingOrders' }\n// )\n//\n// // Low stock alerts\n// const lowStockProducts$ = queryDb(\n// tables.products\n// .select()\n// .where({ isActive: true })\n// .having(tables.products.column('stock').minus(tables.products.column('reservedStock')).lt(10))\n// .orderBy('stock'),\n// { label: 'lowStockProducts' }\n// )";
2
2
  //# sourceMappingURL=ecommerce.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ecommerce.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/ecommerce.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,y+dAkb9B,CAAA"}
1
+ {"version":3,"file":"ecommerce.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/ecommerce.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,q2dAkb9B,CAAA"}