/** * Dark Obsidian TypeScript SDK * Full type definitions for the Dark Obsidian Public API. * * @version 1.0.0 * @docs https://darkobsedian.sameergul.com/docs */ const DEFAULT_BASE_URL = 'https://qatxonlxvtgxvqjgfxpl.supabase.co/functions/v1/api'; // ─── Types ──────────────────────────────────────────────────────────────────── export interface DarkObsidianConfig { apiKey: string; baseUrl?: string; } export interface PaginationMeta { page: number; per_page: number; total: number; total_pages: number; } export interface PaginatedResponse { data: T[]; meta: PaginationMeta; } export interface Category { id: string; name: string; } export interface StockByWarehouse { warehouse_id: string; warehouse_name: string | null; quantity: number; } export interface Product { id: string; name: string; sku: string; price: number; cost_price: number | null; unit: string; description: string | null; barcode: string | null; is_active: boolean; category: Category | null; stock: number; stock_by_warehouse: StockByWarehouse[]; created_at: string; updated_at: string | null; } export interface CreateProductParams { name: string; price: number; cost_price?: number; category_id?: string; description?: string; barcode?: string; unit?: string; sku?: string; } export interface UpdateProductParams extends Partial { is_active?: boolean; } export interface ListProductsParams { search?: string; category_id?: string; in_stock?: boolean; is_active?: boolean; page?: number; per_page?: number; } export interface OrderItem { product_id: string; quantity: number; price?: number; discount?: number; } export interface CustomerInfo { name?: string; email?: string; phone?: string; } export interface CreateOrderParams { customer?: CustomerInfo; items: OrderItem[]; payment_method?: 'cash' | 'card' | 'bank_transfer' | 'other'; tax_amount?: number; shipping_amount?: number; discount_amount?: number; send_invoice?: boolean; warehouse_id?: string; notes?: string; } export interface OrderResponseItem { id: string; product_id: string; product_name: string | null; sku: string | null; quantity: number; unit_price: number; discount: number; total: number; } export interface Order { order_id: string; order_number: string; status: string; payment_status: string; payment_method: string; customer_id: string | null; subtotal: number; tax_amount: number; shipping_amount: number; discount_amount: number; total: number; notes: string | null; items: OrderResponseItem[]; created_at: string; } export interface CreateOrderResponse extends Order { loyalty_points_earned: number; loyalty_transaction_id: string | null; invoice_id: string | null; invoice_number: string | null; } export interface Customer { id: string; business_id: string; name: string; email: string | null; phone: string | null; address: string | null; city: string | null; country: string | null; notes: string | null; is_active: boolean; loyalty_points: number; total_orders: number; total_spent: number; last_purchase_date: string | null; created_at: string; updated_at: string | null; } export interface CreateCustomerParams { name: string; email?: string; phone?: string; address?: string; city?: string; country?: string; notes?: string; } export interface InventoryItem { product_id: string; product_name: string; sku: string; warehouse_id: string; warehouse_name: string | null; quantity: number; min_quantity: number | null; updated_at: string; } export interface AdjustInventoryParams { product_id: string; warehouse_id?: string; quantity: number; reason?: string; } export interface TransferInventoryParams { product_id: string; from_warehouse_id: string; to_warehouse_id: string; quantity: number; } export interface InvoiceItem { id?: string; product_id?: string; description: string; quantity: number; unit_price: number; discount?: number; total: number; } export interface Invoice { id: string; business_id: string; customer_id: string | null; invoice_number: string; subtotal: number; tax_amount: number; discount_amount: number; total: number; status: 'pending' | 'paid' | 'overdue' | 'cancelled'; due_date: string; notes: string | null; created_at: string; invoice_items?: InvoiceItem[]; } export interface CreateInvoiceParams { customer_id: string; items: InvoiceItem[]; due_date?: string; notes?: string; tax_amount?: number; discount_amount?: number; } export interface DashboardAnalytics { period_days: number; revenue: { total: number; orders: number; avg_order_value: number; }; customers: { total: number; new_in_period: number; }; products: { total: number; }; } export interface RevenueDataPoint { date: string; revenue: number; orders: number; } export interface TopProduct { product_id: string; name: string; sku: string; total_sold: number; total_revenue: number; } export interface CustomerAnalytics { total: number; new_in_period: number; top_customers: Pick[]; } export interface AIAnalysisResult { question: string; answer: string; source: 'ai' | 'database'; provider?: string; } export interface LoyaltyTransaction { id: string; customer_id: string; type: 'earn' | 'redeem'; points: number; reference_id: string | null; description: string; created_at: string; } export interface CustomerLoyalty { customer_id: string; customer_name: string; customer_email: string | null; points_balance: number; history: LoyaltyTransaction[]; } export interface RedeemPointsResult { customer_id: string; points_redeemed: number; points_remaining: number; monetary_value: number; transaction_id: string; } export interface WebhookEndpoint { id: string; url: string; events: string[]; is_active: boolean; created_at: string; last_fired_at: string | null; } export interface CreateWebhookParams { url: string; events: string[]; secret?: string; } // ─── Error ──────────────────────────────────────────────────────────────────── export class DarkObsidianError extends Error { code: string; status: number; docs?: string; constructor(code: string, message: string, status: number, docs?: string) { super(message); this.name = 'DarkObsidianError'; this.code = code; this.status = status; this.docs = docs; } } // ─── Base Resource ──────────────────────────────────────────────────────────── class BaseResource { protected _client: DarkObsidian; constructor(client: DarkObsidian) { this._client = client; } protected async _request( method: string, path: string, options: { body?: unknown; params?: Record } = {} ): Promise<{ success: boolean; data: T; meta?: PaginationMeta }> { return this._client['_request'](method, path, options); } } // ─── Resource Classes ───────────────────────────────────────────────────────── export class ProductsResource extends BaseResource { async list(options: ListProductsParams = {}): Promise { const res = await this._request('GET', '/products', { params: options as Record }); return res.data; } async get(id: string): Promise { const res = await this._request('GET', `/products/${id}`); return res.data; } async create(product: CreateProductParams): Promise { const res = await this._request('POST', '/products', { body: product }); return res.data; } async update(id: string, updates: UpdateProductParams): Promise { const res = await this._request('PUT', `/products/${id}`, { body: updates }); return res.data; } async delete(id: string): Promise<{ id: string; deleted: boolean }> { const res = await this._request<{ id: string; deleted: boolean }>('DELETE', `/products/${id}`); return res.data; } } export class OrdersResource extends BaseResource { async list(options: { customer_id?: string; status?: string; from?: string; to?: string; page?: number; per_page?: number; } = {}): Promise { const res = await this._request('GET', '/orders', { params: options as Record }); return res.data; } async get(id: string): Promise { const res = await this._request('GET', `/orders/${id}`); return res.data; } /** * Create a complete sale. Dark Obsidian automatically: * - Deducts stock from warehouse * - Finds or creates the customer * - Awards loyalty points (1 per currency unit) * - Creates invoice (if send_invoice: true) * - Updates financial reports * - Fires sale.completed webhook */ async create(order: CreateOrderParams): Promise { const res = await this._request('POST', '/orders', { body: order }); return res.data; } } export class CustomersResource extends BaseResource { async list(options: { search?: string; is_active?: boolean; page?: number; per_page?: number; } = {}): Promise { const res = await this._request('GET', '/customers', { params: options as Record }); return res.data; } async get(id: string): Promise { const res = await this._request('GET', `/customers/${id}`); return res.data; } async create(customer: CreateCustomerParams): Promise { const res = await this._request('POST', '/customers', { body: customer }); return res.data; } async update(id: string, updates: Partial & { is_active?: boolean }): Promise { const res = await this._request('PUT', `/customers/${id}`, { body: updates }); return res.data; } async delete(id: string): Promise<{ id: string; deleted: boolean }> { const res = await this._request<{ id: string; deleted: boolean }>('DELETE', `/customers/${id}`); return res.data; } } export class InventoryResource extends BaseResource { async list(options: { warehouse_id?: string; low_stock?: boolean; page?: number; per_page?: number; } = {}): Promise { const res = await this._request('GET', '/inventory', { params: options as Record }); return res.data; } async adjust(params: AdjustInventoryParams): Promise<{ product_id: string; warehouse_id: string; quantity: number; adjusted: boolean }> { const res = await this._request<{ product_id: string; warehouse_id: string; quantity: number; adjusted: boolean }>('POST', '/inventory/adjust', { body: params }); return res.data; } async transfer(params: TransferInventoryParams): Promise<{ transfer_id: string; product_id: string; quantity: number; transferred: boolean }> { const res = await this._request<{ transfer_id: string; product_id: string; quantity: number; transferred: boolean }>('POST', '/inventory/transfer', { body: params }); return res.data; } } export class InvoicesResource extends BaseResource { async list(options: { status?: string; customer_id?: string; from?: string; to?: string; page?: number; per_page?: number; } = {}): Promise { const res = await this._request('GET', '/invoices', { params: options as Record }); return res.data; } async get(id: string): Promise { const res = await this._request('GET', `/invoices/${id}`); return res.data; } async create(invoice: CreateInvoiceParams): Promise { const res = await this._request('POST', '/invoices', { body: invoice }); return res.data; } async updateStatus(id: string, status: Invoice['status']): Promise { const res = await this._request('PUT', `/invoices/${id}`, { body: { status } }); return res.data; } } export class AnalyticsResource extends BaseResource { async dashboard(options: { days?: number } = {}): Promise { const res = await this._request('GET', '/analytics/dashboard', { params: options as Record }); return res.data; } async revenue(options: { days?: number } = {}): Promise { const res = await this._request('GET', '/analytics/revenue', { params: options as Record }); return res.data; } async topProducts(options: { days?: number; limit?: number } = {}): Promise { const res = await this._request('GET', '/analytics/top-products', { params: options as Record }); return res.data; } async customers(options: { days?: number } = {}): Promise { const res = await this._request('GET', '/analytics/customers', { params: options as Record }); return res.data; } } export class AIResource extends BaseResource { async analyze(question: string): Promise { const res = await this._request('POST', '/ai/analyze', { body: { question } }); return res.data; } } export class LoyaltyResource extends BaseResource { async getCustomerPoints(customerId: string): Promise { const res = await this._request('GET', `/loyalty/customers/${customerId}`); return res.data; } async redeem(params: { customer_id: string; points: number; sale_id?: string; }): Promise { const res = await this._request('POST', '/loyalty/redeem', { body: params }); return res.data; } } export class WebhooksResource extends BaseResource { async list(options: { page?: number; per_page?: number } = {}): Promise { const res = await this._request('GET', '/webhooks', { params: options as Record }); return res.data; } async get(id: string): Promise { const res = await this._request('GET', `/webhooks/${id}`); return res.data; } async create(params: CreateWebhookParams): Promise { const res = await this._request('POST', '/webhooks', { body: params }); return res.data; } async update(id: string, updates: Partial & { is_active?: boolean }): Promise { const res = await this._request('PUT', `/webhooks/${id}`, { body: updates }); return res.data; } async delete(id: string): Promise<{ id: string; deleted: boolean }> { const res = await this._request<{ id: string; deleted: boolean }>('DELETE', `/webhooks/${id}`); return res.data; } } // ─── Main Client ────────────────────────────────────────────────────────────── export class DarkObsidian { private _apiKey: string; private _baseUrl: string; public readonly products: ProductsResource; public readonly orders: OrdersResource; public readonly customers: CustomersResource; public readonly inventory: InventoryResource; public readonly invoices: InvoicesResource; public readonly analytics: AnalyticsResource; public readonly ai: AIResource; public readonly loyalty: LoyaltyResource; public readonly webhooks: WebhooksResource; constructor({ apiKey, baseUrl }: DarkObsidianConfig) { if (!apiKey) throw new Error('apiKey is required'); this._apiKey = apiKey; this._baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, ''); this.products = new ProductsResource(this); this.orders = new OrdersResource(this); this.customers = new CustomersResource(this); this.inventory = new InventoryResource(this); this.invoices = new InvoicesResource(this); this.analytics = new AnalyticsResource(this); this.ai = new AIResource(this); this.loyalty = new LoyaltyResource(this); this.webhooks = new WebhooksResource(this); } private async _request( method: string, path: string, { body, params }: { body?: unknown; params?: Record } = {} ): Promise<{ success: boolean; data: T; meta?: PaginationMeta }> { let url = `${this._baseUrl}${path}`; if (params && Object.keys(params).length > 0) { const qs = new URLSearchParams(); for (const [k, v] of Object.entries(params)) { if (v !== undefined && v !== null) qs.set(k, String(v)); } url += `?${qs.toString()}`; } const headers: Record = { Authorization: `Bearer ${this._apiKey}`, 'Content-Type': 'application/json', }; const fetchOptions: RequestInit = { method, headers }; if (body !== undefined) { fetchOptions.body = JSON.stringify(body); } const res = await fetch(url, fetchOptions); const json = await res.json(); if (!res.ok || json.success === false) { const e = json.error || {}; throw new DarkObsidianError(e.code || 'API_ERROR', e.message || `HTTP ${res.status}`, res.status, e.docs); } return json; } } export default DarkObsidian;