UNPKG

oneie

Version:

Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.

606 lines (499 loc) 19.9 kB
--- title: Schema dimension: connections category: schema.md tags: ai, frontend related_dimensions: events, groups, knowledge, people, things scope: global created: 2025-11-03 updated: 2025-11-03 version: 1.0.0 ai_context: | This document is part of the connections dimension in the schema.md category. Location: one/connections/schema.md Purpose: Documents one platform - convex schema (plain convex) Related dimensions: events, groups, knowledge, people, things For AI agents: Read this to understand schema. --- # ONE Platform - Convex Schema (Plain Convex) **Version**: 3.0.0 (Frontend Complete) **Status**: Plain Convex schema - no external dependencies **Hybrid Approach**: 25 connection types (18 specific + 7 consolidated), 55 event types (24 base + 20 frontend + 11 consolidated) **Updated**: 2025-01-16 (added frontend entities, auth, organizations, UI preferences) --- ## Schema Definition ```typescript // convex/schema.ts import { defineSchema, defineTable } from "convex/server"; import { v } from "convex/values"; export default defineSchema({ // ============================================================================ // ENTITIES: All objects in the ONE universe (66 types) // ============================================================================ entities: defineTable({ // Universal fields type: v.union( // Core entities (4) v.literal("creator"), v.literal("ai_clone"), v.literal("audience_member"), v.literal("organization"), // Business function agents (10) v.literal("strategy_agent"), v.literal("research_agent"), v.literal("marketing_agent"), v.literal("sales_agent"), v.literal("service_agent"), v.literal("design_agent"), v.literal("engineering_agent"), v.literal("finance_agent"), v.literal("legal_agent"), v.literal("intelligence_agent"), // Content types (7) v.literal("blog_post"), v.literal("video"), v.literal("podcast"), v.literal("social_post"), v.literal("email"), v.literal("course"), v.literal("lesson"), // Products (4) v.literal("digital_product"), v.literal("membership"), v.literal("consultation"), v.literal("nft"), // Community (3) v.literal("community"), v.literal("conversation"), v.literal("message"), // Token (2) v.literal("token"), v.literal("token_contract"), // Knowledge (2) v.literal("knowledge_item"), v.literal("embedding"), // Platform (6) v.literal("website"), v.literal("landing_page"), v.literal("template"), v.literal("livestream"), v.literal("recording"), v.literal("media_asset"), // Business (7) v.literal("payment"), v.literal("subscription"), v.literal("invoice"), v.literal("metric"), v.literal("insight"), v.literal("prediction"), v.literal("report"), // Authentication & Session (5) v.literal("session"), v.literal("oauth_account"), v.literal("verification_token"), v.literal("password_reset_token"), v.literal("ui_preferences"), // Marketing (6) v.literal("notification"), v.literal("email_campaign"), v.literal("announcement"), v.literal("referral"), v.literal("campaign"), v.literal("lead"), // External Integrations (3) v.literal("external_agent"), v.literal("external_workflow"), v.literal("external_connection"), // Protocol Entities (2) v.literal("mandate"), v.literal("product") ), name: v.string(), // Type-specific properties stored as JSON properties: v.any(), // Status tracking status: v.optional(v.union( v.literal("active"), v.literal("inactive"), v.literal("draft"), v.literal("published"), v.literal("archived") )), // Metadata createdAt: v.number(), updatedAt: v.number(), deletedAt: v.optional(v.number()), }) // Indexes for efficient queries .index("by_type", ["type"]) .index("by_status", ["status"]) .index("by_type_status", ["type", "status"]) .index("by_created", ["createdAt"]) .index("by_updated", ["updatedAt"]) // Search index for entities .searchIndex("search_entities", { searchField: "name", filterFields: ["type", "status"] }), // ============================================================================ // CONNECTIONS: All relationships (25 types - 18 specific + 7 consolidated) // ============================================================================ connections: defineTable({ fromEntityId: v.id("entities"), toEntityId: v.id("entities"), relationshipType: v.union( // Ownership (2) v.literal("owns"), v.literal("created_by"), // AI Relationships (3) v.literal("clone_of"), v.literal("trained_on"), v.literal("powers"), // Content Relationships (5) v.literal("authored"), v.literal("generated_by"), v.literal("published_to"), v.literal("part_of"), v.literal("references"), // Community Relationships (4) v.literal("member_of"), v.literal("following"), v.literal("moderates"), v.literal("participated_in"), // Business Relationships (3) v.literal("manages"), v.literal("reports_to"), v.literal("collaborates_with"), // Token Relationships (3) v.literal("holds_tokens"), v.literal("staked_in"), v.literal("earned_from"), // Product Relationships (4) v.literal("purchased"), v.literal("enrolled_in"), v.literal("completed"), v.literal("teaching"), // CONSOLIDATED TYPES (7 - use metadata for variants + protocol) v.literal("transacted"), // Payment/subscription/invoice (metadata.transactionType + protocol) v.literal("notified"), // Notifications (metadata.channel + notificationType) v.literal("referred"), // Referrals (metadata.referralType) v.literal("communicated"), // Agent/protocol communication (metadata.protocol + messageType) v.literal("delegated"), // Task/workflow delegation (metadata.protocol + taskType) v.literal("approved"), // Approvals (metadata.approvalType + protocol) v.literal("fulfilled") // Fulfillment (metadata.fulfillmentType + protocol) ), // Metadata for type-specific data and consolidated types metadata: v.optional(v.any()), createdAt: v.number(), deletedAt: v.optional(v.number()), }) // Indexes for relationship queries .index("from_entity", ["fromEntityId"]) .index("to_entity", ["toEntityId"]) .index("from_type", ["fromEntityId", "relationshipType"]) .index("to_type", ["toEntityId", "relationshipType"]) .index("bidirectional", ["fromEntityId", "toEntityId", "relationshipType"]) .index("by_created", ["createdAt"]), // ============================================================================ // EVENTS: All actions (55 types - 44 specific + 11 consolidated) // ============================================================================ events: defineTable({ type: v.union( // ENTITY LIFECYCLE (4) v.literal("entity_created"), v.literal("entity_updated"), v.literal("entity_deleted"), v.literal("entity_archived"), // USER EVENTS (5) v.literal("user_registered"), v.literal("user_verified"), v.literal("user_login"), v.literal("user_logout"), v.literal("profile_updated"), // AUTHENTICATION EVENTS (6) v.literal("password_reset_requested"), v.literal("password_reset_completed"), v.literal("email_verification_sent"), v.literal("email_verified"), v.literal("two_factor_enabled"), v.literal("two_factor_disabled"), // ORGANIZATION EVENTS (5) v.literal("organization_created"), v.literal("organization_updated"), v.literal("user_invited_to_org"), v.literal("user_joined_org"), v.literal("user_removed_from_org"), // DASHBOARD & UI EVENTS (4) v.literal("dashboard_viewed"), v.literal("settings_updated"), v.literal("theme_changed"), v.literal("preferences_updated"), // AI/CLONE EVENTS (4) v.literal("clone_created"), v.literal("clone_updated"), v.literal("voice_cloned"), v.literal("appearance_cloned"), // AGENT EVENTS (4) v.literal("agent_created"), v.literal("agent_executed"), v.literal("agent_completed"), v.literal("agent_failed"), // TOKEN EVENTS (7) v.literal("token_created"), v.literal("token_minted"), v.literal("token_burned"), v.literal("tokens_purchased"), v.literal("tokens_staked"), v.literal("tokens_unstaked"), v.literal("tokens_transferred"), // COURSE EVENTS (5) v.literal("course_created"), v.literal("course_enrolled"), v.literal("lesson_completed"), v.literal("course_completed"), v.literal("certificate_earned"), // ANALYTICS EVENTS (5) v.literal("metric_calculated"), v.literal("insight_generated"), v.literal("prediction_made"), v.literal("optimization_applied"), v.literal("report_generated"), // CONSOLIDATED EVENTS (11 - use metadata for variants + protocol) v.literal("content_event"), // metadata.action: created|updated|deleted|viewed|shared|liked v.literal("payment_event"), // metadata.status: requested|verified|processed + protocol v.literal("subscription_event"), // metadata.action: started|renewed|cancelled v.literal("commerce_event"), // metadata.eventType + protocol (ACP, AP2) v.literal("livestream_event"), // metadata.status: started|ended + metadata.action: joined|left|chat|donation v.literal("notification_event"), // metadata.channel: email|sms|push|in_app + deliveryStatus v.literal("referral_event"), // metadata.action: created|completed|rewarded v.literal("communication_event"), // metadata.protocol (A2A, ACP, AG-UI) + messageType v.literal("task_event"), // metadata.action: delegated|completed|failed + protocol v.literal("mandate_event"), // metadata.mandateType: intent|cart + protocol (AP2) v.literal("price_event") // metadata.action: checked|changed ), actorId: v.id("entities"), // Who/what caused this targetId: v.optional(v.id("entities")), // Optional target entity timestamp: v.number(), // When it happened metadata: v.any(), // Event-specific data (includes action/status for consolidated types) }) // Indexes for event queries .index("by_type", ["type"]) .index("by_actor", ["actorId"]) .index("by_target", ["targetId"]) .index("by_timestamp", ["timestamp"]) .index("by_actor_type", ["actorId", "type"]) .index("by_target_type", ["targetId", "type"]), // ============================================================================ // TAGS: All categories // ============================================================================ tags: defineTable({ entityId: v.id("entities"), key: v.string(), value: v.string(), }) .index("by_entity", ["entityId"]) .index("by_key", ["key"]) .index("by_key_value", ["key", "value"]), }); ``` --- ## Key Changes from Convex Ents ### 1. Imports **Before (Convex Ents)**: ```typescript import { defineEnt, defineEntSchema, getEntDefinitions } from "convex-ents"; const schema = defineEntSchema({...}); export const entDefinitions = getEntDefinitions(schema); ``` **After (Plain Convex)** ✅: ```typescript import { defineSchema, defineTable } from "convex/server"; export default defineSchema({...}); ``` ### 2. Table Definitions **Before (Convex Ents)**: ```typescript entities: defineEnt({ type: v.string(), name: v.string(), }) .field("type", v.string(), { index: true }) .edges("outgoingConnections", { to: "connections", field: "fromEntityId" }) ``` **After (Plain Convex)** ✅: ```typescript entities: defineTable({ type: v.union(v.literal("creator"), /*...*/), name: v.string(), }) .index("by_type", ["type"]) ``` ### 3. Relationship Queries **Before (Convex Ents Magic)**: ```typescript const creator = await ctx.db.get(creatorId); const owned = await creator.edge("outgoingConnections"); ``` **After (Plain Convex - Explicit)** ✅: ```typescript const creator = await ctx.db.get(creatorId); const ownedConnections = await ctx.db .query("connections") .withIndex("from_type", (q) => q.eq("fromEntityId", creatorId).eq("relationshipType", "owns") ) .collect(); ``` --- ## Type Optimizations ### Connection Types: 25 types (18 specific + 7 consolidated) **Consolidated Connection Types**: 1. `transacted` - Payment/subscription/invoice relationships - Use `metadata.transactionType: "payment" | "subscription" | "invoice"` - Use `metadata.protocol` for protocol-specific transactions 2. `referred` - All referral relationships - Use `metadata.referralType: "direct" | "conversion" | "campaign"` 3. `notified` - All notification channels - Use `metadata.channel: "email" | "sms" | "push" | "in_app"` 4. `communicated` - Agent/protocol communication - Use `metadata.protocol: "a2a" | "acp" | "ag-ui"` - Use `metadata.messageType` for message classification 5. `delegated` - Task/workflow delegation - Use `metadata.protocol` for protocol identification - Use `metadata.taskType` for task classification 6. `approved` - All approval types - Use `metadata.approvalType` + `metadata.protocol` 7. `fulfilled` - All fulfillment types - Use `metadata.fulfillmentType` + `metadata.protocol` ### Event Types: 55 types (44 specific + 11 consolidated) **Frontend-Specific Event Types** (NEW in v3.0.0): - **Authentication Events (6)**: password_reset_requested, password_reset_completed, email_verification_sent, email_verified, two_factor_enabled, two_factor_disabled - **Organization Events (5)**: organization_created, organization_updated, user_invited_to_org, user_joined_org, user_removed_from_org - **Dashboard & UI Events (4)**: dashboard_viewed, settings_updated, theme_changed, preferences_updated **Consolidated Event Types**: 1. `content_event` - All content actions - Use `metadata.action: "created" | "updated" | "deleted" | "viewed" | "shared" | "liked"` 2. `payment_event` - All payment events - Use `metadata.status: "requested" | "verified" | "processed"` - Use `metadata.protocol` for protocol-specific payments (X402, AP2) 3. `subscription_event` - Subscription lifecycle - Use `metadata.action: "started" | "renewed" | "cancelled"` 4. `commerce_event` - Commerce events across protocols - Use `metadata.eventType` + `metadata.protocol: "acp" | "ap2"` 5. `livestream_event` - Livestream events - Use `metadata.status: "started" | "ended"` + `metadata.action: "joined" | "left" | "chat" | "donation"` 6. `notification_event` - All notification delivery - Use `metadata.channel: "email" | "sms" | "push" | "in_app"` - Use `metadata.deliveryStatus` 7. `referral_event` - Referral lifecycle - Use `metadata.action: "created" | "completed" | "rewarded"` 8. `communication_event` - Protocol communication - Use `metadata.protocol: "a2a" | "acp" | "ag-ui"` - Use `metadata.messageType` 9. `task_event` - Task delegation and completion - Use `metadata.action: "delegated" | "completed" | "failed"` - Use `metadata.protocol` for protocol-specific tasks 10. `mandate_event` - AP2 mandate events - Use `metadata.mandateType: "intent" | "cart"` - Use `metadata.protocol: "ap2"` 11. `price_event` - Price checking and changes - Use `metadata.action: "checked" | "changed"` --- ## Query Patterns (Effect.ts) ### Simple Entity Query ```typescript import { confect } from "convex-helpers/server/confect"; import { Effect } from "effect"; import { ConvexDatabase, NotFoundError } from "./services/database"; export const getCreator = confect.query({ args: { creatorId: v.id("entities") }, handler: (ctx, args) => Effect.gen(function* () { const db = yield* ConvexDatabase; const creator = yield* db.get(args.creatorId); if (!creator || creator.type !== "creator") { return yield* Effect.fail(new NotFoundError("Creator not found")); } return creator; }).pipe(Effect.provide(MainLayer)) }); ``` ### Relationship Query ```typescript export const getOwnedContent = confect.query({ args: { creatorId: v.id("entities") }, handler: (ctx, args) => Effect.gen(function* () { const db = yield* ConvexDatabase; // Get connections using index const ownedConnections = yield* db .query("connections") .withIndex("from_type", (q) => q .eq("fromEntityId", args.creatorId) .eq("relationshipType", "owns") ) .collect(); // Get all owned entities const ownedEntities = yield* Effect.all( ownedConnections.map((conn) => db.get(conn.toEntityId)) ); return ownedEntities.filter(Boolean); }).pipe(Effect.provide(MainLayer)) }); ``` ### Metadata-Based Query ```typescript export const getSuccessfulPayments = confect.query({ args: { userId: v.id("entities") }, handler: (ctx, args) => Effect.gen(function* () { const db = yield* ConvexDatabase; // Query by type, filter by metadata const allPayments = yield* db .query("events") .withIndex("by_actor_type", (q) => q.eq("actorId", args.userId).eq("type", "payment_processed") ) .collect(); // Filter by metadata.status const successfulPayments = allPayments.filter( (event) => event.metadata?.status === "completed" ); return successfulPayments; }).pipe(Effect.provide(MainLayer)) }); ``` --- ## Migration Checklist ### Phase 1: Schema Update - [x] Replace `defineEnt` with `defineTable` - [x] Replace `defineEntSchema` with `defineSchema` - [x] Remove `.field()` and `.edges()` methods - [x] Add explicit `.index()` for all queries - [x] Update to 66 entity types (add external integrations + protocol entities + auth + organizations + UI preferences) - [x] Update to 25 connection types (18 specific + 7 consolidated) - [x] Update to 55 event types (44 specific + 11 consolidated - includes auth, org, UI events) ### Phase 2: Query Migration - [ ] Replace `.edge()` with `.query().withIndex()` - [ ] Wrap all queries in Effect.ts - [ ] Add typed error channels - [ ] Update all relationship traversals ### Phase 3: Cleanup - [ ] Remove `convex-ents` package - [ ] Remove all Ents imports - [ ] Update all code examples - [ ] Test all queries --- ## Benefits ✅ **No External Dependency** - Remove `convex-ents` package ✅ **More Explicit** - See exactly what queries do ✅ **Better TypeScript** - Full control over types ✅ **Protocol-Agnostic Design** - All protocols map via metadata.protocol ✅ **Hybrid Type Strategy** - 25 connections (18 specific + 7 consolidated), 55 events (44 specific + 11 consolidated) ✅ **Infinite Extensibility** - Add new protocols without schema changes ✅ **Same Performance** - Direct Convex index usage ✅ **Frontend Complete** - Multi-tenant organizations, Better Auth integration, UI preferences ✅ **Dashboard Ready** - Full support for platform_owner, org_owner, org_user, customer roles --- **Status**: Production ready. Frontend complete with multi-tenant support, authentication, and UI customization. See `one/connections/migration-guide.md` for step-by-step instructions.