diff --git a/docs/packages/entity-prefixing.md b/docs/packages/entity-prefixing.md new file mode 100644 index 000000000..9b365ec68 --- /dev/null +++ b/docs/packages/entity-prefixing.md @@ -0,0 +1,156 @@ +# Entity Prefixing for Package Database Operations + +## Overview + +To prevent entity name collisions between packages in the generated Prisma schema, MetaBuilder uses a standardized entity prefixing system. When schemas are generated from package YAML definitions, entity names are automatically prefixed. + +## Prefix Format + +| Component | Format | Example | +|-----------|--------|---------| +| Entity Name | `Pkg_{PascalPackage}_{EntityName}` | `Pkg_ForumForge_ForumPost` | +| Table Name | `{package_id}_{lowercaseentity}` | `forum_forge_forumpost` | + +### Examples + +| Package | Entity | Prefixed Entity | Table Mapping | +|---------|--------|-----------------|---------------| +| `forum_forge` | `ForumCategory` | `Pkg_ForumForge_ForumCategory` | `forum_forge_forumcategory` | +| `forum_forge` | `ForumThread` | `Pkg_ForumForge_ForumThread` | `forum_forge_forumthread` | +| `social_hub` | `SocialProfile` | `Pkg_SocialHub_SocialProfile` | `social_hub_socialprofile` | +| `github_tools` | `GitHubRepository` | `Pkg_GithubTools_GitHubRepository` | `github_tools_githubrepository` | + +## TypeScript (Schema Generation) + +The prefixing is handled automatically during schema generation by `tools/codegen/schema-registry.ts`: + +```typescript +import { getPrefixedEntityName, entityToPrisma } from './schema-registry' + +// Get prefixed name +const prefixed = getPrefixedEntityName('forum_forge', 'ForumPost') +// => 'Pkg_ForumForge_ForumPost' + +// Generate Prisma model with prefix +const prismaModel = entityToPrisma(schemaEntity, 'forum_forge') +``` + +### CLI Commands + +```bash +# Show prefixed entity names for a package +npx tsx tools/codegen/schema-cli.ts prefix forum_forge + +# Preview generated Prisma models with prefixes +npx tsx tools/codegen/schema-cli.ts preview forum_forge +``` + +## Lua (Runtime Operations) + +For Lua DB operations, use the shared prefix module: + +### Simple Usage + +```lua +local prefix = require('shared.db.prefix') + +-- Package ID for entity prefixing +local PACKAGE_ID = 'forum_forge' + +-- Helper function +local function entity(name) + return prefix.getPrefixedName(PACKAGE_ID, name) +end + +-- Use in DBAL calls +function M.createPost(dbal, params) + return dbal:create(entity('ForumPost'), { + tenantId = params.tenantId, + content = params.content, + -- ... + }) +end + +function M.listPosts(dbal, threadId) + return dbal:list(entity('ForumPost'), { + where = { threadId = threadId }, + }) +end +``` + +### Wrapper-Based Usage + +For simpler code, use the wrapper that auto-prefixes all entity names: + +```lua +local prefix = require('shared.db.prefix') + +-- Create a wrapped DBAL instance +local db = prefix.createPackageDb(dbal, 'forum_forge') + +-- All calls automatically prefix entity names +function M.createPost(params) + return db:create('ForumPost', { -- Automatically becomes 'Pkg_ForumForge_ForumPost' + tenantId = params.tenantId, + content = params.content, + }) +end +``` + +## Schema YAML Files + +Schema YAML files in `packages/{name}/seed/schema/*.yaml` use **unprefixed** entity names. The prefixing is applied during generation: + +```yaml +# packages/forum_forge/seed/schema/forum.yaml +entities: + ForumPost: # Unprefixed in YAML + fields: + id: + type: String + primaryKey: true + content: + type: String + threadId: + type: String + relations: + thread: + kind: belongsTo + target: ForumThread # Also unprefixed - same-package targets auto-resolve + foreignKey: threadId +``` + +When generated: +- Entity becomes `Pkg_ForumForge_ForumPost` +- Relation target becomes `Pkg_ForumForge_ForumThread` +- Table mapping is `@@map("forum_forge_forumpost")` + +## Cross-Package Relations + +For relations to entities in other packages, use the full prefixed name: + +```yaml +# packages/social_hub/seed/schema/social.yaml +entities: + SocialProfile: + relations: + user: + kind: belongsTo + target: User # Core entity (no prefix) + foreignKey: userId +``` + +## Implementation Files + +| File | Purpose | +|------|---------| +| [tools/codegen/schema-registry.ts](../tools/codegen/schema-registry.ts) | TypeScript prefix functions | +| [tools/codegen/schema-cli.ts](../tools/codegen/schema-cli.ts) | CLI commands including `prefix` | +| [packages/shared/seed/scripts/db/prefix.lua](../packages/shared/seed/scripts/db/prefix.lua) | Lua prefix helper module | + +## Benefits + +1. **No Collisions**: Two packages can have entities named `Config` without conflict +2. **Clear Ownership**: Prefixed names immediately show which package owns an entity +3. **Database Isolation**: Table names are namespaced by package +4. **Simple Code**: Lua helper means package code uses simple names internally