@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 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,
|
|
1
|
+
{"version":3,"file":"ecommerce.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/ecommerce.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,q2dAkb9B,CAAA"}
|
|
@@ -22,7 +22,7 @@ export const tables = {
|
|
|
22
22
|
deletedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
23
23
|
},
|
|
24
24
|
}),
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
categories: State.SQLite.table({
|
|
27
27
|
name: 'categories',
|
|
28
28
|
columns: {
|
|
@@ -37,7 +37,7 @@ export const tables = {
|
|
|
37
37
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
38
38
|
},
|
|
39
39
|
}),
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
productCategories: State.SQLite.table({
|
|
42
42
|
name: 'product_categories',
|
|
43
43
|
columns: {
|
|
@@ -46,7 +46,7 @@ export const tables = {
|
|
|
46
46
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
47
47
|
},
|
|
48
48
|
}),
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
customers: State.SQLite.table({
|
|
51
51
|
name: 'customers',
|
|
52
52
|
columns: {
|
|
@@ -59,7 +59,7 @@ export const tables = {
|
|
|
59
59
|
lastOrderAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
60
60
|
},
|
|
61
61
|
}),
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
addresses: State.SQLite.table({
|
|
64
64
|
name: 'addresses',
|
|
65
65
|
columns: {
|
|
@@ -78,7 +78,7 @@ export const tables = {
|
|
|
78
78
|
isDefault: State.SQLite.boolean({ default: false }),
|
|
79
79
|
},
|
|
80
80
|
}),
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
orders: State.SQLite.table({
|
|
83
83
|
name: 'orders',
|
|
84
84
|
columns: {
|
|
@@ -88,19 +88,19 @@ export const tables = {
|
|
|
88
88
|
status: State.SQLite.text({ default: 'draft' }), // draft, pending, paid, processing, shipped, delivered, cancelled
|
|
89
89
|
paymentStatus: State.SQLite.text({ default: 'pending' }), // pending, paid, failed, refunded
|
|
90
90
|
fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }), // unfulfilled, partial, fulfilled
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
subtotal: State.SQLite.integer(), // In cents
|
|
93
93
|
taxAmount: State.SQLite.integer({ default: 0 }),
|
|
94
94
|
shippingAmount: State.SQLite.integer({ default: 0 }),
|
|
95
95
|
discountAmount: State.SQLite.integer({ default: 0 }),
|
|
96
96
|
total: State.SQLite.integer(),
|
|
97
97
|
currency: State.SQLite.text({ default: 'USD' }),
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
shippingAddress: State.SQLite.text(), // JSON
|
|
100
100
|
billingAddress: State.SQLite.text(), // JSON
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
notes: State.SQLite.text({ nullable: true }),
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
105
105
|
updatedAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
106
106
|
cancelledAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
@@ -108,7 +108,7 @@ export const tables = {
|
|
|
108
108
|
deliveredAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),
|
|
109
109
|
},
|
|
110
110
|
}),
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
orderItems: State.SQLite.table({
|
|
113
113
|
name: 'order_items',
|
|
114
114
|
columns: {
|
|
@@ -124,7 +124,7 @@ export const tables = {
|
|
|
124
124
|
fulfillmentStatus: State.SQLite.text({ default: 'unfulfilled' }),
|
|
125
125
|
},
|
|
126
126
|
}),
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
// Inventory tracking with event sourcing
|
|
129
129
|
inventoryEvents: State.SQLite.table({
|
|
130
130
|
name: 'inventory_events',
|
|
@@ -138,7 +138,7 @@ export const tables = {
|
|
|
138
138
|
createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),
|
|
139
139
|
},
|
|
140
140
|
}),
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
// Shopping cart (client-side state)
|
|
143
143
|
cart: State.SQLite.clientDocument({
|
|
144
144
|
name: 'cart',
|
|
@@ -151,8 +151,8 @@ export const tables = {
|
|
|
151
151
|
discountCode: Schema.NullOr(Schema.String),
|
|
152
152
|
notes: Schema.String,
|
|
153
153
|
}),
|
|
154
|
-
default: {
|
|
155
|
-
id: SessionIdSymbol,
|
|
154
|
+
default: {
|
|
155
|
+
id: SessionIdSymbol,
|
|
156
156
|
value: { items: [], discountCode: null, notes: '' }
|
|
157
157
|
},
|
|
158
158
|
}),
|
|
@@ -172,7 +172,7 @@ export const events = {
|
|
|
172
172
|
createdAt: Schema.Date,
|
|
173
173
|
}),
|
|
174
174
|
}),
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
productUpdated: Events.synced({
|
|
177
177
|
name: 'v1.ProductUpdated',
|
|
178
178
|
schema: Schema.Struct({
|
|
@@ -183,7 +183,7 @@ export const events = {
|
|
|
183
183
|
updatedAt: Schema.Date,
|
|
184
184
|
}),
|
|
185
185
|
}),
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
productStockAdjusted: Events.synced({
|
|
188
188
|
name: 'v1.ProductStockAdjusted',
|
|
189
189
|
schema: Schema.Struct({
|
|
@@ -194,12 +194,12 @@ export const events = {
|
|
|
194
194
|
createdAt: Schema.Date,
|
|
195
195
|
}),
|
|
196
196
|
}),
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
productDeactivated: Events.synced({
|
|
199
199
|
name: 'v1.ProductDeactivated',
|
|
200
200
|
schema: Schema.Struct({ id: Schema.String }),
|
|
201
201
|
}),
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
// Customer management
|
|
204
204
|
customerCreated: Events.synced({
|
|
205
205
|
name: 'v1.CustomerCreated',
|
|
@@ -211,7 +211,7 @@ export const events = {
|
|
|
211
211
|
createdAt: Schema.Date,
|
|
212
212
|
}),
|
|
213
213
|
}),
|
|
214
|
-
|
|
214
|
+
|
|
215
215
|
// Order lifecycle with state machine
|
|
216
216
|
orderCreated: Events.synced({
|
|
217
217
|
name: 'v1.OrderCreated',
|
|
@@ -233,7 +233,7 @@ export const events = {
|
|
|
233
233
|
createdAt: Schema.Date,
|
|
234
234
|
}),
|
|
235
235
|
}),
|
|
236
|
-
|
|
236
|
+
|
|
237
237
|
orderPaymentReceived: Events.synced({
|
|
238
238
|
name: 'v1.OrderPaymentReceived',
|
|
239
239
|
schema: Schema.Struct({
|
|
@@ -244,7 +244,7 @@ export const events = {
|
|
|
244
244
|
paidAt: Schema.Date,
|
|
245
245
|
}),
|
|
246
246
|
}),
|
|
247
|
-
|
|
247
|
+
|
|
248
248
|
orderShipped: Events.synced({
|
|
249
249
|
name: 'v1.OrderShipped',
|
|
250
250
|
schema: Schema.Struct({
|
|
@@ -254,7 +254,7 @@ export const events = {
|
|
|
254
254
|
shippedAt: Schema.Date,
|
|
255
255
|
}),
|
|
256
256
|
}),
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
orderDelivered: Events.synced({
|
|
259
259
|
name: 'v1.OrderDelivered',
|
|
260
260
|
schema: Schema.Struct({
|
|
@@ -262,7 +262,7 @@ export const events = {
|
|
|
262
262
|
deliveredAt: Schema.Date,
|
|
263
263
|
}),
|
|
264
264
|
}),
|
|
265
|
-
|
|
265
|
+
|
|
266
266
|
orderCancelled: Events.synced({
|
|
267
267
|
name: 'v1.OrderCancelled',
|
|
268
268
|
schema: Schema.Struct({
|
|
@@ -271,7 +271,7 @@ export const events = {
|
|
|
271
271
|
cancelledAt: Schema.Date,
|
|
272
272
|
}),
|
|
273
273
|
}),
|
|
274
|
-
|
|
274
|
+
|
|
275
275
|
// Cart management (local)
|
|
276
276
|
cartUpdated: tables.cart.set,
|
|
277
277
|
}
|
|
@@ -281,58 +281,58 @@ const materializers = State.SQLite.materializers(events, {
|
|
|
281
281
|
// Product materializers
|
|
282
282
|
'v1.ProductCreated': ({ id, sku, name, description, price, currency, createdAt }) =>
|
|
283
283
|
tables.products.insert({ id, sku, name, description, price, currency, createdAt, updatedAt: createdAt }),
|
|
284
|
-
|
|
284
|
+
|
|
285
285
|
'v1.ProductUpdated': ({ id, name, description, price, updatedAt }) =>
|
|
286
|
-
tables.products.update({
|
|
286
|
+
tables.products.update({
|
|
287
287
|
name: name ?? undefined,
|
|
288
|
-
description: description ?? undefined,
|
|
288
|
+
description: description ?? undefined,
|
|
289
289
|
price: price ?? undefined,
|
|
290
|
-
updatedAt
|
|
290
|
+
updatedAt
|
|
291
291
|
}).where({ id }),
|
|
292
|
-
|
|
292
|
+
|
|
293
293
|
'v1.ProductStockAdjusted': ({ productId, adjustment, reason, referenceId, createdAt }) => [
|
|
294
294
|
// Record the inventory event
|
|
295
|
-
tables.inventoryEvents.insert({
|
|
295
|
+
tables.inventoryEvents.insert({
|
|
296
296
|
id: crypto.randomUUID(),
|
|
297
|
-
productId,
|
|
297
|
+
productId,
|
|
298
298
|
type: 'adjustment',
|
|
299
|
-
quantity: adjustment,
|
|
300
|
-
reason,
|
|
301
|
-
referenceId,
|
|
302
|
-
createdAt
|
|
299
|
+
quantity: adjustment,
|
|
300
|
+
reason,
|
|
301
|
+
referenceId,
|
|
302
|
+
createdAt
|
|
303
303
|
}),
|
|
304
304
|
// Update product stock (eventually consistent)
|
|
305
|
-
tables.products.update({
|
|
305
|
+
tables.products.update({
|
|
306
306
|
stock: Math.max(0, tables.products.select('stock').where({ id: productId }).scalar() + adjustment),
|
|
307
307
|
updatedAt: createdAt
|
|
308
308
|
}).where({ id: productId }),
|
|
309
309
|
],
|
|
310
|
-
|
|
310
|
+
|
|
311
311
|
'v1.ProductDeactivated': ({ id }) =>
|
|
312
312
|
tables.products.update({ isActive: false }).where({ id }),
|
|
313
|
-
|
|
313
|
+
|
|
314
314
|
// Customer materializers
|
|
315
315
|
'v1.CustomerCreated': ({ id, email, firstName, lastName, createdAt }) =>
|
|
316
316
|
tables.customers.insert({ id, email, firstName, lastName, createdAt }),
|
|
317
|
-
|
|
317
|
+
|
|
318
318
|
// Order materializers with inventory reservation
|
|
319
319
|
'v1.OrderCreated': ({ id, orderNumber, customerId, items, subtotal, total, shippingAddress, billingAddress, createdAt }) => [
|
|
320
320
|
// Create the order
|
|
321
|
-
tables.orders.insert({
|
|
322
|
-
id,
|
|
323
|
-
orderNumber,
|
|
324
|
-
customerId,
|
|
321
|
+
tables.orders.insert({
|
|
322
|
+
id,
|
|
323
|
+
orderNumber,
|
|
324
|
+
customerId,
|
|
325
325
|
status: 'pending',
|
|
326
|
-
subtotal,
|
|
327
|
-
total,
|
|
326
|
+
subtotal,
|
|
327
|
+
total,
|
|
328
328
|
shippingAddress: JSON.stringify(shippingAddress),
|
|
329
329
|
billingAddress: JSON.stringify(billingAddress),
|
|
330
|
-
createdAt,
|
|
331
|
-
updatedAt: createdAt
|
|
330
|
+
createdAt,
|
|
331
|
+
updatedAt: createdAt
|
|
332
332
|
}),
|
|
333
333
|
// Create order items and reserve inventory
|
|
334
334
|
...items.flatMap(item => [
|
|
335
|
-
tables.orderItems.insert({
|
|
335
|
+
tables.orderItems.insert({
|
|
336
336
|
id: crypto.randomUUID(),
|
|
337
337
|
orderId: id,
|
|
338
338
|
productId: item.productId,
|
|
@@ -343,52 +343,52 @@ const materializers = State.SQLite.materializers(events, {
|
|
|
343
343
|
totalPrice: item.quantity * item.unitPrice,
|
|
344
344
|
}),
|
|
345
345
|
// Reserve stock
|
|
346
|
-
tables.products.update({
|
|
346
|
+
tables.products.update({
|
|
347
347
|
reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() + item.quantity
|
|
348
348
|
}).where({ id: item.productId }),
|
|
349
349
|
]),
|
|
350
350
|
],
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
'v1.OrderPaymentReceived': ({ orderId, amount, paymentMethod, transactionId, paidAt }) =>
|
|
353
|
-
tables.orders.update({
|
|
354
|
-
status: 'paid',
|
|
353
|
+
tables.orders.update({
|
|
354
|
+
status: 'paid',
|
|
355
355
|
paymentStatus: 'paid',
|
|
356
|
-
updatedAt: paidAt
|
|
356
|
+
updatedAt: paidAt
|
|
357
357
|
}).where({ id: orderId }),
|
|
358
|
-
|
|
358
|
+
|
|
359
359
|
'v1.OrderShipped': ({ orderId, trackingNumber, carrier, shippedAt }) => [
|
|
360
|
-
tables.orders.update({
|
|
360
|
+
tables.orders.update({
|
|
361
361
|
status: 'shipped',
|
|
362
362
|
fulfillmentStatus: 'fulfilled',
|
|
363
363
|
shippedAt,
|
|
364
|
-
updatedAt: shippedAt
|
|
364
|
+
updatedAt: shippedAt
|
|
365
365
|
}).where({ id: orderId }),
|
|
366
366
|
// Convert reserved stock to actual stock reduction
|
|
367
|
-
...tables.orderItems.select().where({ orderId }).map(item =>
|
|
368
|
-
tables.products.update({
|
|
367
|
+
...tables.orderItems.select().where({ orderId }).map(item =>
|
|
368
|
+
tables.products.update({
|
|
369
369
|
stock: tables.products.select('stock').where({ id: item.productId }).scalar() - item.quantity,
|
|
370
370
|
reservedStock: tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity
|
|
371
371
|
}).where({ id: item.productId })
|
|
372
372
|
),
|
|
373
373
|
],
|
|
374
|
-
|
|
374
|
+
|
|
375
375
|
'v1.OrderDelivered': ({ orderId, deliveredAt }) =>
|
|
376
|
-
tables.orders.update({
|
|
376
|
+
tables.orders.update({
|
|
377
377
|
status: 'delivered',
|
|
378
378
|
deliveredAt,
|
|
379
|
-
updatedAt: deliveredAt
|
|
379
|
+
updatedAt: deliveredAt
|
|
380
380
|
}).where({ id: orderId }),
|
|
381
|
-
|
|
381
|
+
|
|
382
382
|
'v1.OrderCancelled': ({ orderId, reason, cancelledAt }) => [
|
|
383
|
-
tables.orders.update({
|
|
383
|
+
tables.orders.update({
|
|
384
384
|
status: 'cancelled',
|
|
385
385
|
cancelledAt,
|
|
386
386
|
notes: reason,
|
|
387
|
-
updatedAt: cancelledAt
|
|
387
|
+
updatedAt: cancelledAt
|
|
388
388
|
}).where({ id: orderId }),
|
|
389
389
|
// Release reserved inventory
|
|
390
|
-
...tables.orderItems.select().where({ orderId }).map(item =>
|
|
391
|
-
tables.products.update({
|
|
390
|
+
...tables.orderItems.select().where({ orderId }).map(item =>
|
|
391
|
+
tables.products.update({
|
|
392
392
|
reservedStock: Math.max(0, tables.products.select('reservedStock').where({ id: item.productId }).scalar() - item.quantity)
|
|
393
393
|
}).where({ id: item.productId })
|
|
394
394
|
),
|
|
@@ -416,7 +416,7 @@ export const schema = makeSchema({ events, state })
|
|
|
416
416
|
// tables.orders
|
|
417
417
|
// .select()
|
|
418
418
|
// .join(tables.customers, 'customerId', 'id')
|
|
419
|
-
// .where({
|
|
419
|
+
// .where({
|
|
420
420
|
// 'orders.status': 'paid',
|
|
421
421
|
// 'orders.fulfillmentStatus': 'unfulfilled'
|
|
422
422
|
// })
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const socialSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Social network with activity feeds and real-time interactions\nexport const tables = {\n users: State.SQLite.table({\n name: 'users',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n username: State.SQLite.text(),\n email: State.SQLite.text(),\n displayName: State.SQLite.text(),\n bio: State.SQLite.text({ nullable: true }),\n avatarUrl: State.SQLite.text({ nullable: true }),\n isVerified: State.SQLite.boolean({ default: false }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n lastActiveAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n // Privacy settings\n isPrivate: State.SQLite.boolean({ default: false }),\n allowsFollowers: State.SQLite.boolean({ default: true }),\n },\n }),\n \n posts: State.SQLite.table({\n name: 'posts',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n authorId: State.SQLite.text(),\n content: State.SQLite.text(),\n mediaUrls: State.SQLite.text({ nullable: true }), // JSON array of media URLs\n replyToId: State.SQLite.text({ nullable: true }), // For threading\n visibility: State.SQLite.text({ default: 'public' }), // public, followers, private\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 follows: State.SQLite.table({\n name: 'follows',\n columns: {\n followerId: State.SQLite.text(),\n followingId: State.SQLite.text(),\n status: State.SQLite.text({ default: 'active' }), // active, pending, blocked\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n approvedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n \n likes: State.SQLite.table({\n name: 'likes',\n columns: {\n userId: State.SQLite.text(),\n postId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n // Aggregate tables for performance (eventually consistent)\n postStats: State.SQLite.table({\n name: 'post_stats',\n columns: {\n postId: State.SQLite.text({ primaryKey: true }),\n likeCount: State.SQLite.integer({ default: 0 }),\n replyCount: State.SQLite.integer({ default: 0 }),\n shareCount: State.SQLite.integer({ default: 0 }),\n lastUpdated: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n userStats: State.SQLite.table({\n name: 'user_stats', \n columns: {\n userId: State.SQLite.text({ primaryKey: true }),\n followerCount: State.SQLite.integer({ default: 0 }),\n followingCount: State.SQLite.integer({ default: 0 }),\n postCount: State.SQLite.integer({ default: 0 }),\n lastUpdated: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n \n // Client-side state for UI\n feedState: State.SQLite.clientDocument({\n name: 'feedState',\n schema: Schema.Struct({\n currentFeed: Schema.Literal('home', 'discover', 'following'),\n lastRefresh: Schema.Date,\n scrollPosition: Schema.Number,\n }),\n default: { \n id: SessionIdSymbol, \n value: { currentFeed: 'home', lastRefresh: new Date(), scrollPosition: 0 }\n },\n }),\n}\n\nexport const events = {\n // User events\n userCreated: Events.synced({\n name: 'v1.UserCreated',\n schema: Schema.Struct({\n id: Schema.String,\n username: Schema.String,\n email: Schema.String,\n displayName: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n \n userProfileUpdated: Events.synced({\n name: 'v1.UserProfileUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n displayName: Schema.NullOr(Schema.String),\n bio: Schema.NullOr(Schema.String),\n avatarUrl: Schema.NullOr(Schema.String),\n }),\n }),\n \n userPrivacySettingsChanged: Events.synced({\n name: 'v1.UserPrivacySettingsChanged',\n schema: Schema.Struct({\n id: Schema.String,\n isPrivate: Schema.Boolean,\n allowsFollowers: Schema.Boolean,\n }),\n }),\n \n userLastActiveUpdated: Events.synced({\n name: 'v1.UserLastActiveUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n lastActiveAt: Schema.Date,\n }),\n }),\n \n // Post events\n postCreated: Events.synced({\n name: 'v1.PostCreated',\n schema: Schema.Struct({\n id: Schema.String,\n authorId: Schema.String,\n content: Schema.String,\n mediaUrls: Schema.NullOr(Schema.Array(Schema.String)),\n replyToId: Schema.NullOr(Schema.String),\n visibility: Schema.Literal('public', 'followers', 'private'),\n createdAt: Schema.Date,\n }),\n }),\n \n postEdited: Events.synced({\n name: 'v1.PostEdited',\n schema: Schema.Struct({\n id: Schema.String,\n content: Schema.String,\n editedAt: Schema.Date,\n }),\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 // Social interaction events\n followRequested: Events.synced({\n name: 'v1.FollowRequested',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n \n followApproved: Events.synced({\n name: 'v1.FollowApproved',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n approvedAt: Schema.Date,\n }),\n }),\n \n unfollowed: Events.synced({\n name: 'v1.Unfollowed',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n }),\n }),\n \n postLiked: Events.synced({\n name: 'v1.PostLiked',\n schema: Schema.Struct({\n userId: Schema.String,\n postId: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n \n postUnliked: Events.synced({\n name: 'v1.PostUnliked',\n schema: Schema.Struct({\n userId: Schema.String,\n postId: Schema.String,\n }),\n }),\n \n // Local UI state\n feedStateUpdated: tables.feedState.set,\n}\n\n// Materializers with eventual consistency for aggregates\nconst materializers = State.SQLite.materializers(events, {\n // User materializers\n 'v1.UserCreated': ({ id, username, email, displayName, createdAt }) => [\n tables.users.insert({ id, username, email, displayName, createdAt }),\n tables.userStats.insert({ userId: id, lastUpdated: createdAt }),\n ],\n \n 'v1.UserProfileUpdated': ({ id, displayName, bio, avatarUrl }) =>\n tables.users.update({ displayName, bio, avatarUrl }).where({ id }),\n \n 'v1.UserPrivacySettingsChanged': ({ id, isPrivate, allowsFollowers }) =>\n tables.users.update({ isPrivate, allowsFollowers }).where({ id }),\n \n 'v1.UserLastActiveUpdated': ({ id, lastActiveAt }) =>\n tables.users.update({ lastActiveAt }).where({ id }),\n \n // Post materializers\n 'v1.PostCreated': ({ id, authorId, content, mediaUrls, replyToId, visibility, createdAt }) => [\n tables.posts.insert({ \n id, \n authorId, \n content, \n mediaUrls: mediaUrls ? JSON.stringify(mediaUrls) : null,\n replyToId, \n visibility, \n createdAt \n }),\n tables.postStats.insert({ postId: id, lastUpdated: createdAt }),\n // Update user post count\n tables.userStats.update({ \n postCount: tables.userStats.select('postCount').where({ userId: authorId }).scalar() + 1,\n lastUpdated: createdAt\n }).where({ userId: authorId }),\n ],\n \n 'v1.PostEdited': ({ id, content, editedAt }) =>\n tables.posts.update({ content, editedAt }).where({ id }),\n \n 'v1.PostDeleted': ({ id, deletedAt }) =>\n tables.posts.update({ deletedAt }).where({ id }),\n \n // Follow materializers\n 'v1.FollowRequested': ({ followerId, followingId, createdAt }) =>\n tables.follows.insert({ followerId, followingId, status: 'pending', createdAt }),\n \n 'v1.FollowApproved': ({ followerId, followingId, approvedAt }) => [\n tables.follows.update({ status: 'active', approvedAt }).where({ followerId, followingId }),\n // Update follower counts\n tables.userStats.update({ \n followerCount: tables.userStats.select('followerCount').where({ userId: followingId }).scalar() + 1,\n lastUpdated: approvedAt\n }).where({ userId: followingId }),\n tables.userStats.update({ \n followingCount: tables.userStats.select('followingCount').where({ userId: followerId }).scalar() + 1,\n lastUpdated: approvedAt\n }).where({ userId: followerId }),\n ],\n \n 'v1.Unfollowed': ({ followerId, followingId }) => [\n tables.follows.delete().where({ followerId, followingId }),\n // Update follower counts (eventual consistency)\n tables.userStats.update({ \n followerCount: Math.max(0, tables.userStats.select('followerCount').where({ userId: followingId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ userId: followingId }),\n tables.userStats.update({ \n followingCount: Math.max(0, tables.userStats.select('followingCount').where({ userId: followerId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ userId: followerId }),\n ],\n \n // Like materializers (idempotent)\n 'v1.PostLiked': ({ userId, postId, createdAt }) => [\n tables.likes.insert({ userId, postId, createdAt }),\n // Update like count\n tables.postStats.update({ \n likeCount: tables.postStats.select('likeCount').where({ postId }).scalar() + 1,\n lastUpdated: createdAt\n }).where({ postId }),\n ],\n \n 'v1.PostUnliked': ({ userId, postId }) => [\n tables.likes.delete().where({ userId, postId }),\n // Update like count\n tables.postStats.update({ \n likeCount: Math.max(0, tables.postStats.select('likeCount').where({ postId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ postId }),\n ],\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n\n// Example queries for activity feeds:\n//\n// // Home feed - posts from followed users\n// const homeFeed$ = (userId: string) => queryDb(\n// tables.posts\n// .select()\n// .join(tables.follows, 'authorId', 'followingId')\n// .join(tables.users, 'authorId', 'id')\n// .leftJoin(tables.postStats, 'id', 'postId')\n// .where({ \n// followerId: userId, \n// 'follows.status': 'active',\n// 'posts.deletedAt': null,\n// 'posts.visibility': ['public', 'followers']\n// })\n// .orderBy('createdAt', 'desc')\n// .limit(50),\n// { label: `homeFeed-${userId}` }\n// )\n//\n// // User profile with stats\n// const userProfile$ = (username: string) => queryDb(\n// tables.users\n// .select()\n// .leftJoin(tables.userStats, 'id', 'userId')\n// .where({ username }),\n// { label: `userProfile-${username}` }\n// )";
|
|
1
|
+
export declare const socialSchemaContent = "import { Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'\n\n// Social network with activity feeds and real-time interactions\nexport const tables = {\n users: State.SQLite.table({\n name: 'users',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n username: State.SQLite.text(),\n email: State.SQLite.text(),\n displayName: State.SQLite.text(),\n bio: State.SQLite.text({ nullable: true }),\n avatarUrl: State.SQLite.text({ nullable: true }),\n isVerified: State.SQLite.boolean({ default: false }),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n lastActiveAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n // Privacy settings\n isPrivate: State.SQLite.boolean({ default: false }),\n allowsFollowers: State.SQLite.boolean({ default: true }),\n },\n }),\n\n posts: State.SQLite.table({\n name: 'posts',\n columns: {\n id: State.SQLite.text({ primaryKey: true }),\n authorId: State.SQLite.text(),\n content: State.SQLite.text(),\n mediaUrls: State.SQLite.text({ nullable: true }), // JSON array of media URLs\n replyToId: State.SQLite.text({ nullable: true }), // For threading\n visibility: State.SQLite.text({ default: 'public' }), // public, followers, private\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 follows: State.SQLite.table({\n name: 'follows',\n columns: {\n followerId: State.SQLite.text(),\n followingId: State.SQLite.text(),\n status: State.SQLite.text({ default: 'active' }), // active, pending, blocked\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n approvedAt: State.SQLite.integer({ nullable: true, schema: Schema.DateFromNumber }),\n },\n }),\n\n likes: State.SQLite.table({\n name: 'likes',\n columns: {\n userId: State.SQLite.text(),\n postId: State.SQLite.text(),\n createdAt: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n // Aggregate tables for performance (eventually consistent)\n postStats: State.SQLite.table({\n name: 'post_stats',\n columns: {\n postId: State.SQLite.text({ primaryKey: true }),\n likeCount: State.SQLite.integer({ default: 0 }),\n replyCount: State.SQLite.integer({ default: 0 }),\n shareCount: State.SQLite.integer({ default: 0 }),\n lastUpdated: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n userStats: State.SQLite.table({\n name: 'user_stats',\n columns: {\n userId: State.SQLite.text({ primaryKey: true }),\n followerCount: State.SQLite.integer({ default: 0 }),\n followingCount: State.SQLite.integer({ default: 0 }),\n postCount: State.SQLite.integer({ default: 0 }),\n lastUpdated: State.SQLite.integer({ schema: Schema.DateFromNumber }),\n },\n }),\n\n // Client-side state for UI\n feedState: State.SQLite.clientDocument({\n name: 'feedState',\n schema: Schema.Struct({\n currentFeed: Schema.Literal('home', 'discover', 'following'),\n lastRefresh: Schema.Date,\n scrollPosition: Schema.Number,\n }),\n default: {\n id: SessionIdSymbol,\n value: { currentFeed: 'home', lastRefresh: new Date(), scrollPosition: 0 }\n },\n }),\n}\n\nexport const events = {\n // User events\n userCreated: Events.synced({\n name: 'v1.UserCreated',\n schema: Schema.Struct({\n id: Schema.String,\n username: Schema.String,\n email: Schema.String,\n displayName: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n userProfileUpdated: Events.synced({\n name: 'v1.UserProfileUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n displayName: Schema.NullOr(Schema.String),\n bio: Schema.NullOr(Schema.String),\n avatarUrl: Schema.NullOr(Schema.String),\n }),\n }),\n\n userPrivacySettingsChanged: Events.synced({\n name: 'v1.UserPrivacySettingsChanged',\n schema: Schema.Struct({\n id: Schema.String,\n isPrivate: Schema.Boolean,\n allowsFollowers: Schema.Boolean,\n }),\n }),\n\n userLastActiveUpdated: Events.synced({\n name: 'v1.UserLastActiveUpdated',\n schema: Schema.Struct({\n id: Schema.String,\n lastActiveAt: Schema.Date,\n }),\n }),\n\n // Post events\n postCreated: Events.synced({\n name: 'v1.PostCreated',\n schema: Schema.Struct({\n id: Schema.String,\n authorId: Schema.String,\n content: Schema.String,\n mediaUrls: Schema.NullOr(Schema.Array(Schema.String)),\n replyToId: Schema.NullOr(Schema.String),\n visibility: Schema.Literal('public', 'followers', 'private'),\n createdAt: Schema.Date,\n }),\n }),\n\n postEdited: Events.synced({\n name: 'v1.PostEdited',\n schema: Schema.Struct({\n id: Schema.String,\n content: Schema.String,\n editedAt: Schema.Date,\n }),\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 // Social interaction events\n followRequested: Events.synced({\n name: 'v1.FollowRequested',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n followApproved: Events.synced({\n name: 'v1.FollowApproved',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n approvedAt: Schema.Date,\n }),\n }),\n\n unfollowed: Events.synced({\n name: 'v1.Unfollowed',\n schema: Schema.Struct({\n followerId: Schema.String,\n followingId: Schema.String,\n }),\n }),\n\n postLiked: Events.synced({\n name: 'v1.PostLiked',\n schema: Schema.Struct({\n userId: Schema.String,\n postId: Schema.String,\n createdAt: Schema.Date,\n }),\n }),\n\n postUnliked: Events.synced({\n name: 'v1.PostUnliked',\n schema: Schema.Struct({\n userId: Schema.String,\n postId: Schema.String,\n }),\n }),\n\n // Local UI state\n feedStateUpdated: tables.feedState.set,\n}\n\n// Materializers with eventual consistency for aggregates\nconst materializers = State.SQLite.materializers(events, {\n // User materializers\n 'v1.UserCreated': ({ id, username, email, displayName, createdAt }) => [\n tables.users.insert({ id, username, email, displayName, createdAt }),\n tables.userStats.insert({ userId: id, lastUpdated: createdAt }),\n ],\n\n 'v1.UserProfileUpdated': ({ id, displayName, bio, avatarUrl }) =>\n tables.users.update({ displayName, bio, avatarUrl }).where({ id }),\n\n 'v1.UserPrivacySettingsChanged': ({ id, isPrivate, allowsFollowers }) =>\n tables.users.update({ isPrivate, allowsFollowers }).where({ id }),\n\n 'v1.UserLastActiveUpdated': ({ id, lastActiveAt }) =>\n tables.users.update({ lastActiveAt }).where({ id }),\n\n // Post materializers\n 'v1.PostCreated': ({ id, authorId, content, mediaUrls, replyToId, visibility, createdAt }) => [\n tables.posts.insert({\n id,\n authorId,\n content,\n mediaUrls: mediaUrls ? JSON.stringify(mediaUrls) : null,\n replyToId,\n visibility,\n createdAt\n }),\n tables.postStats.insert({ postId: id, lastUpdated: createdAt }),\n // Update user post count\n tables.userStats.update({\n postCount: tables.userStats.select('postCount').where({ userId: authorId }).scalar() + 1,\n lastUpdated: createdAt\n }).where({ userId: authorId }),\n ],\n\n 'v1.PostEdited': ({ id, content, editedAt }) =>\n tables.posts.update({ content, editedAt }).where({ id }),\n\n 'v1.PostDeleted': ({ id, deletedAt }) =>\n tables.posts.update({ deletedAt }).where({ id }),\n\n // Follow materializers\n 'v1.FollowRequested': ({ followerId, followingId, createdAt }) =>\n tables.follows.insert({ followerId, followingId, status: 'pending', createdAt }),\n\n 'v1.FollowApproved': ({ followerId, followingId, approvedAt }) => [\n tables.follows.update({ status: 'active', approvedAt }).where({ followerId, followingId }),\n // Update follower counts\n tables.userStats.update({\n followerCount: tables.userStats.select('followerCount').where({ userId: followingId }).scalar() + 1,\n lastUpdated: approvedAt\n }).where({ userId: followingId }),\n tables.userStats.update({\n followingCount: tables.userStats.select('followingCount').where({ userId: followerId }).scalar() + 1,\n lastUpdated: approvedAt\n }).where({ userId: followerId }),\n ],\n\n 'v1.Unfollowed': ({ followerId, followingId }) => [\n tables.follows.delete().where({ followerId, followingId }),\n // Update follower counts (eventual consistency)\n tables.userStats.update({\n followerCount: Math.max(0, tables.userStats.select('followerCount').where({ userId: followingId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ userId: followingId }),\n tables.userStats.update({\n followingCount: Math.max(0, tables.userStats.select('followingCount').where({ userId: followerId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ userId: followerId }),\n ],\n\n // Like materializers (idempotent)\n 'v1.PostLiked': ({ userId, postId, createdAt }) => [\n tables.likes.insert({ userId, postId, createdAt }),\n // Update like count\n tables.postStats.update({\n likeCount: tables.postStats.select('likeCount').where({ postId }).scalar() + 1,\n lastUpdated: createdAt\n }).where({ postId }),\n ],\n\n 'v1.PostUnliked': ({ userId, postId }) => [\n tables.likes.delete().where({ userId, postId }),\n // Update like count\n tables.postStats.update({\n likeCount: Math.max(0, tables.postStats.select('likeCount').where({ postId }).scalar() - 1),\n lastUpdated: new Date()\n }).where({ postId }),\n ],\n})\n\nconst state = State.SQLite.makeState({ tables, materializers })\n\nexport const schema = makeSchema({ events, state })\n\n// Example queries for activity feeds:\n//\n// // Home feed - posts from followed users\n// const homeFeed$ = (userId: string) => queryDb(\n// tables.posts\n// .select()\n// .join(tables.follows, 'authorId', 'followingId')\n// .join(tables.users, 'authorId', 'id')\n// .leftJoin(tables.postStats, 'id', 'postId')\n// .where({\n// followerId: userId,\n// 'follows.status': 'active',\n// 'posts.deletedAt': null,\n// 'posts.visibility': ['public', 'followers']\n// })\n// .orderBy('createdAt', 'desc')\n// .limit(50),\n// { label: `homeFeed-${userId}` }\n// )\n//\n// // User profile with stats\n// const userProfile$ = (username: string) => queryDb(\n// tables.users\n// .select()\n// .leftJoin(tables.userStats, 'id', 'userId')\n// .where({ username }),\n// { label: `userProfile-${username}` }\n// )";
|
|
2
2
|
//# sourceMappingURL=social.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/social.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../../src/mcp-content/schemas/social.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,u8VAiV3B,CAAA"}
|