rspace-online/modules/rcart/schemas.ts

294 lines
5.9 KiB
TypeScript

/**
* rCart Automerge document schemas.
*
* Two document types:
* - Catalog: one doc per space holding all catalog entries.
* DocId: {space}:cart:catalog
* - Orders: one doc per order (server-validated via Intent/Claim).
* DocId: {space}:cart:orders:{orderId}
*/
import type { DocSchema } from '../../shared/local-first/document';
// ── Document types ──
export interface CatalogEntry {
id: string;
artifactId: string;
artifact: unknown;
title: string;
productType: string | null;
requiredCapabilities: string[];
substrates: string[];
creatorId: string | null;
sourceSpace: string | null;
tags: string[];
status: string;
createdAt: number;
updatedAt: number;
}
export interface CatalogDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
items: Record<string, CatalogEntry>;
}
export interface OrderMeta {
id: string;
catalogEntryId: string;
artifactId: string;
buyerId: string | null;
buyerLocation: string | null;
buyerContact: string | null;
providerId: string | null;
providerName: string | null;
providerDistanceKm: number | null;
quantity: number;
productionCost: number | null;
creatorPayout: number | null;
communityPayout: number | null;
totalPrice: number | null;
currency: string;
status: string;
paymentMethod: string | null;
paymentTx: string | null;
paymentNetwork: string | null;
createdAt: number;
paidAt: number;
acceptedAt: number;
completedAt: number;
updatedAt: number;
}
export interface OrderDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
order: OrderMeta;
}
// ── Schema registration ──
export const catalogSchema: DocSchema<CatalogDoc> = {
module: 'cart',
collection: 'catalog',
version: 1,
init: (): CatalogDoc => ({
meta: {
module: 'cart',
collection: 'catalog',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
items: {},
}),
};
export const orderSchema: DocSchema<OrderDoc> = {
module: 'cart',
collection: 'orders',
version: 1,
init: (): OrderDoc => ({
meta: {
module: 'cart',
collection: 'orders',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
order: {
id: '',
catalogEntryId: '',
artifactId: '',
buyerId: null,
buyerLocation: null,
buyerContact: null,
providerId: null,
providerName: null,
providerDistanceKm: null,
quantity: 1,
productionCost: null,
creatorPayout: null,
communityPayout: null,
totalPrice: null,
currency: 'USD',
status: 'pending',
paymentMethod: null,
paymentTx: null,
paymentNetwork: null,
createdAt: Date.now(),
paidAt: 0,
acceptedAt: 0,
completedAt: 0,
updatedAt: Date.now(),
},
}),
};
// ── Shopping Cart types ──
export type CartStatus = 'OPEN' | 'FUNDING' | 'FUNDED' | 'CHECKING_OUT' | 'ORDERED' | 'CLOSED';
export interface CartItemVendor {
name: string;
domain: string;
platform: string | null; // 'amazon' | 'shopify' | 'etsy' | null
}
export interface CartItem {
name: string;
price: number | null;
currency: string;
quantity: number;
sourceUrl: string;
imageUrl: string | null;
description: string | null;
vendor: CartItemVendor;
addedBy: string | null; // DID
addedAt: number;
sku: string | null;
}
export interface CartContribution {
userId: string | null;
username: string;
amount: number;
currency: string;
paymentMethod: string; // 'MANUAL' for MVP
status: string; // 'pending' | 'confirmed'
txHash: string | null;
createdAt: number;
updatedAt: number;
}
export interface CartEvent {
type: string; // 'item_added' | 'item_removed' | 'contribution' | 'status_change'
actor: string;
detail: string;
timestamp: number;
}
export interface ShoppingCartDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
cart: {
id: string;
name: string;
description: string;
status: CartStatus;
createdBy: string | null;
targetAmount: number;
fundedAmount: number;
currency: string;
createdAt: number;
updatedAt: number;
};
items: Record<string, CartItem>;
contributions: Record<string, CartContribution>;
events: CartEvent[];
}
export interface ShoppingCartIndexEntry {
name: string;
status: CartStatus;
itemCount: number;
totalAmount: number;
fundedAmount: number;
currency: string;
createdAt: number;
updatedAt: number;
}
export interface ShoppingCartIndexDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
carts: Record<string, ShoppingCartIndexEntry>;
}
// ── Shopping Cart schema registration ──
export const shoppingCartSchema: DocSchema<ShoppingCartDoc> = {
module: 'cart',
collection: 'shopping',
version: 1,
init: (): ShoppingCartDoc => ({
meta: {
module: 'cart',
collection: 'shopping',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
cart: {
id: '',
name: '',
description: '',
status: 'OPEN',
createdBy: null,
targetAmount: 0,
fundedAmount: 0,
currency: 'USD',
createdAt: Date.now(),
updatedAt: Date.now(),
},
items: {},
contributions: {},
events: [],
}),
};
export const shoppingCartIndexSchema: DocSchema<ShoppingCartIndexDoc> = {
module: 'cart',
collection: 'shopping-index',
version: 1,
init: (): ShoppingCartIndexDoc => ({
meta: {
module: 'cart',
collection: 'shopping-index',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
carts: {},
}),
};
// ── Helpers ──
export function catalogDocId(space: string) {
return `${space}:cart:catalog` as const;
}
export function orderDocId(space: string, orderId: string) {
return `${space}:cart:orders:${orderId}` as const;
}
export function shoppingCartDocId(space: string, cartId: string) {
return `${space}:cart:shopping:${cartId}` as const;
}
export function shoppingCartIndexDocId(space: string) {
return `${space}:cart:shopping-index` as const;
}