@happyvertical/smrt-profiles 0.30.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.
Files changed (42) hide show
  1. package/AGENTS.md +53 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +176 -0
  5. package/dist/chunks/ApiKey-B2LKEaP8.js +143 -0
  6. package/dist/chunks/ApiKey-B2LKEaP8.js.map +1 -0
  7. package/dist/chunks/ApiKeyCollection-B6Op817e.js +91 -0
  8. package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +1 -0
  9. package/dist/chunks/AuditLogCollection-BYqCj0uE.js +195 -0
  10. package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +1 -0
  11. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +3065 -0
  12. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +1 -0
  13. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js +122 -0
  14. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js.map +1 -0
  15. package/dist/chunks/ProfileCollection-DU6wUJTO.js +782 -0
  16. package/dist/chunks/ProfileCollection-DU6wUJTO.js.map +1 -0
  17. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +120 -0
  18. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +1 -0
  19. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +184 -0
  20. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +1 -0
  21. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js +177 -0
  22. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js.map +1 -0
  23. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +117 -0
  24. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +1 -0
  25. package/dist/chunks/ProfileRelationshipType-BXBLldea.js +103 -0
  26. package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +1 -0
  27. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +48 -0
  28. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +1 -0
  29. package/dist/chunks/index-jFtOWsAV.js +1014 -0
  30. package/dist/chunks/index-jFtOWsAV.js.map +1 -0
  31. package/dist/index.d.ts +1848 -0
  32. package/dist/index.js +70 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/manifest.json +11829 -0
  35. package/dist/smrt-knowledge.json +3846 -0
  36. package/dist/types.d.ts +41 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils.d.ts +61 -0
  40. package/dist/utils.js +49 -0
  41. package/dist/utils.js.map +1 -0
  42. package/package.json +75 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileCollection-DU6wUJTO.js","sources":["../../src/prompts.ts","../../src/models/ProfileType.ts","../../src/models/Profile.ts","../../src/collections/ProfileCollection.ts"],"sourcesContent":["/**\n * Prompt registrations for the @happyvertical/smrt-profiles package.\n *\n * Prompts are registered at module-load time via `definePrompt()` so that\n * tenant-aware overrides can be applied at call time via `resolvePrompt()`.\n *\n * Mirrors the pattern used by `@happyvertical/smrt-content` (see\n * `content-prompts.ts`) and `@happyvertical/smrt-facts` (see `prompts.ts`).\n */\n\nimport {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\n// Bio generation only uses non-PII profile fields (name + description).\n// Email is intentionally NOT passed to the AI provider — see review on PR\n// #1209 — to minimize PII exposure. If a downstream tenant needs email in\n// the bio, they can override the template via PromptOverride.\nexport const smrtProfilesGenerateBioPrompt = definePrompt({\n key: 'smrtProfiles.profile.generateBio',\n template: `Write a short, professional bio for this person.\n\nProfile name: {profileName}\nProfile description: {profileDescription}\n\nReturn only the bio text, with no commentary or surrounding quotation marks.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","/**\n * ProfileType model - Lookup table defining profile types\n *\n * Represents the nature of a profile (e.g., 'human', 'org', 'robot').\n * Uses UUID primary key with unique slug for human-readable lookups.\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ProfileTypeOptions extends SmrtObjectOptions {\n slug?: string;\n name?: string;\n description?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ProfileType extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n // slug is inherited from SmrtObject (auto-generated from name)\n @field({ required: true })\n name: string = '';\n\n description?: string;\n\n constructor(options: ProfileTypeOptions = {}) {\n super(options);\n if (options.name) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Convenience method for slug-based lookup\n *\n * @param slug - The slug to search for\n * @returns ProfileType instance or null if not found\n */\n static async getBySlug(_slug: string): Promise<ProfileType | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * Profile model - Core entity representing any profile type\n *\n * Central table holding all primary entities with type, email, name, and description.\n * Uses UUID primary key with relationships to ProfileType for classification.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport {\n assertValidOwnedAssetRelationship,\n assertValidOwnedAssetSortOrder,\n resolveOwnedAssetsById,\n} from '@happyvertical/smrt-assets';\n\nimport {\n field,\n foreignKey,\n oneToMany,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n promptMessageOptions,\n smrtProfilesGenerateBioPrompt,\n} from '../prompts';\nimport type { ProfileRelationship } from './ProfileRelationship';\nimport { ProfileType } from './ProfileType';\n\nexport interface ProfileOptions extends SmrtObjectOptions {\n typeId?: string;\n email?: string;\n name?: string;\n description?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class Profile extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n @foreignKey('ProfileType', { required: true })\n typeId?: string; // References ProfileType.id\n\n @field({ unique: true })\n email?: string; // Optional email address\n\n @field({ required: true })\n name: string = ''; // Display name\n\n description?: string; // Short bio or description\n\n // Relationships (not stored as columns)\n @oneToMany('ProfileMetadata')\n metadata: any[] = [];\n\n // ProfileRelationship declares multiple foreign keys back to Profile\n // (fromProfileId / toProfileId / contextProfileId), so each oneToMany names\n // its inverse side explicitly. This both disambiguates `loadRelatedMany`\n // and gives the R10-generated `getRelationshipsFrom()` / `getRelationshipsTo()`\n // accessors the correct inverse foreign key.\n @oneToMany('ProfileRelationship', { foreignKey: 'fromProfileId' })\n relationshipsFrom: any[] = [];\n\n @oneToMany('ProfileRelationship', { foreignKey: 'toProfileId' })\n relationshipsTo: any[] = [];\n\n constructor(options: ProfileOptions = {}) {\n super(options);\n if (options.typeId !== undefined) this.typeId = options.typeId;\n if (options.email !== undefined) this.email = options.email;\n if (options.name) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Get the profile type slug for this profile\n *\n * @returns The slug of the profile type\n */\n async getTypeSlug(): Promise<string> {\n const type = await this.loadRelated('typeId');\n return type?.slug || '';\n }\n\n /**\n * Set the profile type by slug\n *\n * @param slug - The slug of the profile type\n * @throws Error if profile type not found\n */\n async setTypeBySlug(slug: string): Promise<void> {\n const type = await ProfileType.getBySlug(slug);\n if (!type) throw new Error(`Profile type '${slug}' not found`);\n this.typeId = type.id as string;\n }\n\n /**\n * Add metadata to this profile\n *\n * @param metafieldSlug - The slug of the metafield\n * @param value - The value to set\n */\n async addMetadata(metafieldSlug: string, value: any): Promise<void> {\n const { ProfileMetafieldCollection } = await import(\n '../collections/ProfileMetafieldCollection'\n );\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n // Get or create metafield collection\n const metafieldCollection = await (\n ProfileMetafieldCollection as any\n ).create(this.options);\n\n // Find the metafield by slug\n const metafield = await metafieldCollection.getBySlug(metafieldSlug);\n if (!metafield) {\n throw new Error(`Metafield '${metafieldSlug}' not found`);\n }\n\n // Validate the value\n await metafield.validateValue(value);\n\n // Get or create metadata collection\n const metadataCollection = await (ProfileMetadataCollection as any).create(\n this.options,\n );\n\n // Check if metadata already exists\n const existing = await metadataCollection.list({\n where: { profileId: this.id, metafieldId: metafield.id },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing\n const metadata = existing[0];\n metadata.value = String(value);\n await metadata.save();\n } else {\n // Create new\n const metadata = await metadataCollection.create({\n profileId: this.id,\n metafieldId: metafield.id,\n value: String(value),\n });\n await metadata.save();\n }\n }\n\n /**\n * Get all metadata for this profile as key-value object\n *\n * @returns Object with metafield slugs as keys\n */\n async getMetadata(): Promise<Record<string, any>> {\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n const metadataCollection = await (ProfileMetadataCollection as any).create(\n this.options,\n );\n\n return await metadataCollection.getMetadataObject(this.id);\n }\n\n /**\n * Update multiple metadata values\n *\n * @param metadata - Object with metafield slugs as keys and values\n */\n async updateMetadata(metadata: Record<string, any>): Promise<void> {\n for (const [metafieldSlug, value] of Object.entries(metadata)) {\n await this.addMetadata(metafieldSlug, value);\n }\n }\n\n /**\n * Remove metadata by metafield slug\n *\n * @param metafieldSlug - The slug of the metafield to remove\n */\n async removeMetadata(metafieldSlug: string): Promise<void> {\n const { ProfileMetafieldCollection } = await import(\n '../collections/ProfileMetafieldCollection'\n );\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n const metafieldCollection = await (\n ProfileMetafieldCollection as any\n ).create(this.options);\n\n const metafield = await metafieldCollection.getBySlug(metafieldSlug);\n if (!metafield) {\n throw new Error(`Metafield '${metafieldSlug}' not found`);\n }\n\n const metadataCollection = await (ProfileMetadataCollection as any).create(\n this.options,\n );\n\n const existing = await metadataCollection.list({\n where: { profileId: this.id, metafieldId: metafield.id },\n limit: 1,\n });\n\n if (existing.length > 0) {\n await existing[0].delete();\n }\n }\n\n private async getProfileAssetCollection() {\n const { ProfileAssetCollection } = await import(\n '../collections/ProfileAssetCollection'\n );\n return ProfileAssetCollection.create({ db: this.db });\n }\n async getAssets(relationship?: string): Promise<Asset[]> {\n if (!this.id) {\n return [];\n }\n\n const profileAssets = await this.getProfileAssetCollection();\n const linkedAssets = await profileAssets.byLeft(\n this.id,\n relationship ? { relationship } : {},\n );\n\n return resolveOwnedAssetsById(\n this.db,\n linkedAssets.map((link) => link.assetId),\n this.tenantId,\n );\n }\n\n async addAsset(\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved profile or asset');\n }\n\n assertValidOwnedAssetRelationship(relationship);\n assertValidOwnedAssetSortOrder(sortOrder);\n\n const profileAssets = await this.getProfileAssetCollection();\n await profileAssets.attach(this.id, asset.id, {\n relationship,\n sortOrder,\n tenantId: this.tenantId,\n });\n }\n\n async removeAsset(assetId: string, relationship?: string): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const profileAssets = await this.getProfileAssetCollection();\n await profileAssets.detach(\n this.id,\n assetId,\n relationship ? { relationship } : {},\n );\n }\n\n /**\n * Add a relationship to another profile\n *\n * @param toProfile - The target profile\n * @param relationshipSlug - The type of relationship\n * @param contextProfile - Optional context profile for tertiary relationships\n */\n async addRelationship(\n toProfile: Profile,\n relationshipSlug: string,\n contextProfile?: Profile,\n ): Promise<void> {\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n const { ProfileRelationshipType } = await import(\n './ProfileRelationshipType'\n );\n\n // Get relationship type\n const relationshipTypeCollection = await (\n ProfileRelationshipTypeCollection as any\n ).create(this.options);\n\n const relationshipType =\n await relationshipTypeCollection.getBySlug(relationshipSlug);\n if (!relationshipType) {\n throw new Error(`Relationship type '${relationshipSlug}' not found`);\n }\n\n // Check if relationship already exists\n const relationshipCollection = await (\n ProfileRelationshipCollection as any\n ).create(this.options);\n\n const exists = await relationshipCollection.exists(\n this.id,\n toProfile.id,\n relationshipType.id,\n );\n\n if (!exists) {\n // Create the relationship\n const relationship = await relationshipCollection.create({\n fromProfileId: this.id,\n toProfileId: toProfile.id,\n typeId: relationshipType.id,\n contextProfileId: contextProfile?.id,\n });\n await relationship.save();\n }\n\n // Handle reciprocal relationships\n if (relationshipType.reciprocal) {\n const handler =\n ProfileRelationshipType.getReciprocalHandler(relationshipSlug);\n if (handler) {\n await handler(this, toProfile, contextProfile);\n }\n }\n }\n\n /**\n * Get all relationships for this profile\n *\n * @param options - Filter options (typeSlug, direction)\n * @returns Array of ProfileRelationship instances\n */\n async getRelationships(options?: {\n typeSlug?: string;\n direction?: 'from' | 'to' | 'all';\n }): Promise<ProfileRelationship[]> {\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n\n const relationshipCollection = await (\n ProfileRelationshipCollection as any\n ).create(this.options);\n\n const direction = options?.direction || 'all';\n\n // Get type ID if typeSlug is provided\n let typeId: string | undefined;\n if (options?.typeSlug) {\n const relationshipTypeCollection = await (\n ProfileRelationshipTypeCollection as any\n ).create(this.options);\n\n const relationshipType = await relationshipTypeCollection.getBySlug(\n options.typeSlug,\n );\n typeId = relationshipType?.id;\n }\n\n // Fetch relationships based on direction\n if (direction === 'from') {\n return await relationshipCollection.getFromProfile(this.id, typeId);\n } else if (direction === 'to') {\n return await relationshipCollection.getToProfile(this.id, typeId);\n } else {\n return await relationshipCollection.getForProfile(this.id, typeId);\n }\n }\n\n /**\n * Get related profiles\n *\n * @param relationshipSlug - Optional filter by relationship type slug\n * @returns Array of related Profile instances\n */\n async getRelatedProfiles(relationshipSlug?: string): Promise<Profile[]> {\n const { ProfileCollection } = await import(\n '../collections/ProfileCollection'\n );\n\n const relationships = await this.getRelationships({\n typeSlug: relationshipSlug,\n direction: 'all',\n });\n\n const profileCollection = await (ProfileCollection as any).create(\n this.options,\n );\n\n const relatedProfiles: Profile[] = [];\n const seenIds = new Set<string>();\n\n for (const relationship of relationships) {\n // Get the other profile (not this one)\n // Convert Field instances to strings for comparison\n const fromId = String(relationship.fromProfileId);\n const toId = String(relationship.toProfileId);\n const thisId = String(this.id);\n const otherId = fromId === thisId ? toId : fromId;\n\n if (!seenIds.has(otherId)) {\n seenIds.add(otherId);\n const profile = await profileCollection.get({ id: otherId });\n if (profile) {\n relatedProfiles.push(profile);\n }\n }\n }\n\n return relatedProfiles;\n }\n\n /**\n * Remove a relationship to another profile\n *\n * @param toProfile - The target profile\n * @param relationshipSlug - The type of relationship to remove\n */\n async removeRelationship(\n toProfile: Profile,\n relationshipSlug: string,\n ): Promise<void> {\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n\n // Get relationship type\n const relationshipTypeCollection = await (\n ProfileRelationshipTypeCollection as any\n ).create(this.options);\n\n const relationshipType =\n await relationshipTypeCollection.getBySlug(relationshipSlug);\n if (!relationshipType) {\n throw new Error(`Relationship type '${relationshipSlug}' not found`);\n }\n\n // Find and delete the relationship\n const relationshipCollection = await (\n ProfileRelationshipCollection as any\n ).create(this.options);\n\n const relationships = await relationshipCollection.list({\n where: {\n fromProfileId: this.id,\n toProfileId: toProfile.id,\n typeId: relationshipType.id,\n },\n });\n\n for (const relationship of relationships) {\n await relationship.delete();\n }\n\n // Handle reciprocal relationships - delete the inverse\n if (relationshipType.reciprocal) {\n const inverseRelationships = await relationshipCollection.list({\n where: {\n fromProfileId: toProfile.id,\n toProfileId: this.id,\n typeId: relationshipType.id,\n },\n });\n\n for (const relationship of inverseRelationships) {\n await relationship.delete();\n }\n }\n }\n\n /**\n * AI-powered: Generate a professional bio for this profile\n *\n * Uses the `smrtProfiles.profile.generateBio` prompt registered via\n * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * @returns Generated bio text\n */\n async generateBio(): Promise<string> {\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias. SmrtClass maps `persistence → db` lazily during `initialize()`,\n // so on a freshly-constructed Profile that has not yet been initialized,\n // `this.options.db` may be undefined while `this.options.persistence` is\n // set. Falling back here ensures stored app- and tenant-level prompt\n // overrides in `_smrt_prompt_overrides` are honored on the first call —\n // before `getAiClient()` triggers full initialization further below.\n const db = this.options.db ?? this.options.persistence;\n\n const resolvedPrompt = await resolvePrompt(\n smrtProfilesGenerateBioPrompt.key,\n {\n db,\n tenantId: this.tenantId,\n variables: {\n profileName: this.name || '',\n profileDescription: this.description || '',\n },\n },\n );\n\n const ai = await this.getAiClient();\n const response = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n\n return response.trim();\n }\n\n /**\n * AI-powered: Check if profile matches criteria\n *\n * @param criteria - Criteria to match against\n * @returns True if matches criteria\n */\n async matches(criteria: string): Promise<boolean> {\n return await this.is(criteria);\n }\n\n /**\n * Find profiles by metadata key-value pair\n *\n * @param metafieldSlug - The metafield slug to search\n * @param value - The value to match\n * @returns Array of matching profiles\n */\n static async findByMetadata(\n _metafieldSlug: string,\n _value: any,\n ): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Find profiles by type slug\n *\n * @param typeSlug - The profile type slug\n * @returns Array of matching profiles\n */\n static async findByType(_typeSlug: string): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Find related profiles for a given profile\n *\n * @param profileId - The profile UUID\n * @param relationshipSlug - Optional filter by relationship type\n * @returns Array of related profiles\n */\n static async findRelated(\n _profileId: string,\n _relationshipSlug?: string,\n ): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Search profiles by email\n *\n * @param email - The email to search for\n * @returns Profile or null if not found\n */\n static async searchByEmail(_email: string): Promise<Profile | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n\n // ========================\n // Auth-related methods\n // ========================\n\n /**\n * Get all API keys for this profile\n *\n * @returns Array of API keys\n */\n async getApiKeys(): Promise<any[]> {\n const { ApiKeyCollection } = await import(\n '../collections/ApiKeyCollection'\n );\n const collection = await (ApiKeyCollection as any).create(this.options);\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Get active (non-revoked, non-expired) API keys for this profile\n *\n * @returns Array of active API keys\n */\n async getActiveApiKeys(): Promise<any[]> {\n const { ApiKeyCollection } = await import(\n '../collections/ApiKeyCollection'\n );\n const collection = await (ApiKeyCollection as any).create(this.options);\n return await collection.findActiveByProfile(this.id as string);\n }\n\n /**\n * Generate a new API key for this profile\n *\n * @param options - Key options (name, scopes, expiration)\n * @returns The generated key (plaintext) and ApiKey record\n */\n async generateApiKey(options: {\n name: string;\n scopes?: string[];\n expiresAt?: Date | null;\n }): Promise<{ key: string; apiKey: any }> {\n const { ApiKey } = await import('./ApiKey');\n return await ApiKey.generate(this, {\n ...options,\n db: this.options?.db,\n });\n }\n\n /**\n * Get all OIDC identities linked to this profile\n *\n * @returns Array of OIDC identity records\n */\n async getOidcIdentities(): Promise<any[]> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await (OidcIdentityCollection as any).create(\n this.options,\n );\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Link a new OIDC identity to this profile\n *\n * @param oidcData - OIDC provider data\n * @returns The linked OIDC identity record\n */\n async linkOidcIdentity(oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n }): Promise<any> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await (OidcIdentityCollection as any).create(\n this.options,\n );\n return await collection.linkToProfile(this, oidcData);\n }\n\n /**\n * Get all Nostr identities linked to this profile\n *\n * @returns Array of Nostr identity records\n */\n async getNostrIdentities(): Promise<any[]> {\n const { NostrIdentityCollection } = await import(\n '../collections/NostrIdentityCollection'\n );\n const collection = await (NostrIdentityCollection as any).create(\n this.options,\n );\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Link a new Nostr identity to this profile\n *\n * @param nostrData - Nostr identity data (encrypted keypair)\n * @returns The linked Nostr identity record\n */\n async linkNostrIdentity(nostrData: {\n pubkey: string;\n encryptedPrivkey: string;\n encryptionIv: string;\n encryptionTag: string;\n email: string;\n nip05Username?: string;\n }): Promise<any> {\n const { NostrIdentityCollection } = await import(\n '../collections/NostrIdentityCollection'\n );\n const collection = await (NostrIdentityCollection as any).create(\n this.options,\n );\n return await collection.linkToProfile(this, nostrData);\n }\n\n /**\n * Get audit logs for actions performed by this profile\n *\n * @param limit - Maximum number of logs to return\n * @returns Array of audit log entries\n */\n async getAuditLogs(limit: number = 50): Promise<any[]> {\n const { AuditLogCollection } = await import(\n '../collections/AuditLogCollection'\n );\n const collection = await (AuditLogCollection as any).create(this.options);\n return await collection.getRecentActivity(this.id as string, limit);\n }\n\n /**\n * Record an audit log entry for an action by this profile\n *\n * @param options - Audit log options\n * @returns The created audit log entry\n */\n async recordAction(options: {\n action: string;\n resourceType: string;\n resourceId: string;\n source?: 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';\n metadata?: Record<string, any>;\n onBehalfOf?: Profile | null;\n }): Promise<any> {\n const { AuditLogCollection } = await import(\n '../collections/AuditLogCollection'\n );\n const collection = await (AuditLogCollection as any).create(this.options);\n return await collection.record({\n profile: this,\n ...options,\n });\n }\n}\n","/**\n * ProfileCollection - Collection manager for Profile objects\n *\n * Provides advanced querying and batch operations for Profile entities.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport {\n addOwnedAssetFromCollection,\n getOwnedAssetsFromCollection,\n removeOwnedAssetFromCollection,\n} from '@happyvertical/smrt-assets';\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Profile } from '../models/Profile';\n\nexport class ProfileCollection extends SmrtCollection<Profile> {\n static readonly _itemClass = Profile;\n\n /**\n * Find a profile by email address\n *\n * @param email - The email address to search for\n * @returns The matching profile or null\n */\n async findByEmail(email: string): Promise<Profile | null> {\n const normalizedEmail = email.toLowerCase();\n const profiles = await this.list({\n where: { email: normalizedEmail },\n limit: 1,\n });\n return profiles.length > 0 ? profiles[0] : null;\n }\n\n /**\n * Find profiles by type slug\n *\n * @param typeSlug - The profile type slug to filter by\n * @returns Array of matching profiles\n */\n async findByType(typeSlug: string): Promise<Profile[]> {\n // Will use eager loading when available\n const allProfiles = await this.list({});\n\n const filtered: Profile[] = [];\n for (const profile of allProfiles) {\n const slug = await profile.getTypeSlug();\n if (slug === typeSlug) {\n filtered.push(profile);\n }\n }\n\n return filtered;\n }\n\n /**\n * Batch get metadata for multiple profiles\n *\n * @param profileIds - Array of profile UUIDs\n * @returns Map of profile ID to metadata object\n */\n async batchGetMetadata(\n profileIds: string[],\n ): Promise<Map<string, Record<string, any>>> {\n const result = new Map<string, Record<string, any>>();\n\n for (const profileId of profileIds) {\n const profile = await this.get({ id: profileId });\n if (profile) {\n const metadata = await profile.getMetadata();\n result.set(profileId, metadata);\n }\n }\n\n return result;\n }\n\n /**\n * Batch update metadata for multiple profiles\n *\n * @param updates - Array of { profileId, data } objects\n */\n async batchUpdateMetadata(\n updates: Array<{ profileId: string; data: Record<string, any> }>,\n ): Promise<void> {\n for (const update of updates) {\n const profile = await this.get({ id: update.profileId });\n if (profile) {\n await profile.updateMetadata(update.data);\n }\n }\n }\n\n /**\n * Find related profiles for a given profile\n *\n * @param profileId - The profile UUID\n * @param relationshipSlug - Optional filter by relationship type\n * @returns Array of related profiles\n */\n async findRelated(\n profileId: string,\n relationshipSlug?: string,\n ): Promise<Profile[]> {\n const profile = await this.get({ id: profileId });\n if (!profile) return [];\n\n return await profile.getRelatedProfiles(relationshipSlug);\n }\n\n async getAssets(profileId: string, relationship?: string): Promise<Asset[]> {\n return getOwnedAssetsFromCollection(this, profileId, relationship);\n }\n\n async addAsset(\n profileId: string,\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n await addOwnedAssetFromCollection(\n this,\n 'Profile',\n profileId,\n asset,\n relationship,\n sortOrder,\n );\n }\n\n async removeAsset(\n profileId: string,\n assetId: string,\n relationship?: string,\n ): Promise<void> {\n await removeOwnedAssetFromCollection(\n this,\n 'Profile',\n profileId,\n assetId,\n relationship,\n );\n }\n\n /**\n * Get the relationship network for a profile up to a maximum depth\n *\n * @param profileId - The starting profile UUID\n * @param options - Configuration options\n * @returns Map of profile ID to depth level\n */\n async getRelationshipNetwork(\n profileId: string,\n options: { maxDepth?: number } = {},\n ): Promise<Map<string, number>> {\n const maxDepth = options.maxDepth || 2;\n const network = new Map<string, number>();\n const visited = new Set<string>();\n const queue: Array<{ id: string; depth: number }> = [\n { id: profileId, depth: 0 },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift();\n if (!current) {\n break;\n }\n\n if (visited.has(current.id) || current.depth > maxDepth) {\n continue;\n }\n\n visited.add(current.id);\n network.set(current.id, current.depth);\n\n if (current.depth < maxDepth) {\n const related = await this.findRelated(current.id);\n for (const profile of related) {\n if (profile.id && !visited.has(profile.id)) {\n queue.push({ id: profile.id, depth: current.depth + 1 });\n }\n }\n }\n }\n\n return network;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenant-scoped helper methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find all profiles belonging to a specific tenant\n *\n * @param tenantId - The tenant UUID to filter by\n * @returns Array of profiles for this tenant\n */\n async findByTenant(tenantId: string): Promise<Profile[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global profiles (without a tenant)\n *\n * @returns Array of profiles with null tenantId\n */\n async findGlobal(): Promise<Profile[]> {\n return this.list({ where: { tenantId: null } });\n }\n\n /**\n * Find profiles belonging to a tenant plus all global profiles\n *\n * @param tenantId - The tenant UUID to include\n * @returns Array of tenant-specific and global profiles\n */\n async findWithGlobals(tenantId: string): Promise<Profile[]> {\n return this.query(\n `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,\n [tenantId],\n );\n }\n}\n"],"names":["__decorateClass","ProfileCollection","tenantId"],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import { foreignKey, field, smrt, SmrtObject, SmrtCollection } from "@happyvertical/smrt-core";
2
+ import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
+ if (kind && result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ let ProfileMetadata = class extends SmrtObject {
14
+ tenantId = null;
15
+ profileId;
16
+ metafieldId;
17
+ value = "";
18
+ // The metadata value (stored as text)
19
+ constructor(options = {}) {
20
+ super(options);
21
+ if (options.profileId !== void 0) this.profileId = options.profileId;
22
+ if (options.metafieldId !== void 0)
23
+ this.metafieldId = options.metafieldId;
24
+ if (options.value) this.value = options.value;
25
+ }
26
+ /**
27
+ * Validate this metadata value against the metafield's validation schema
28
+ *
29
+ * @returns True if valid, throws error if invalid
30
+ */
31
+ async validate() {
32
+ const metafield = await this.loadRelated("metafieldId");
33
+ if (!metafield) {
34
+ throw new Error("Metafield not found");
35
+ }
36
+ return await metafield.validateValue(this.value);
37
+ }
38
+ /**
39
+ * Get the metafield slug for this metadata
40
+ *
41
+ * @returns The slug of the metafield
42
+ */
43
+ async getMetafieldSlug() {
44
+ const metafield = await this.loadRelated("metafieldId");
45
+ return metafield?.slug || "";
46
+ }
47
+ };
48
+ __decorateClass([
49
+ tenantId({ nullable: true })
50
+ ], ProfileMetadata.prototype, "tenantId", 2);
51
+ __decorateClass([
52
+ foreignKey("Profile", { required: true })
53
+ ], ProfileMetadata.prototype, "profileId", 2);
54
+ __decorateClass([
55
+ foreignKey("ProfileMetafield", { required: true })
56
+ ], ProfileMetadata.prototype, "metafieldId", 2);
57
+ __decorateClass([
58
+ field({ required: true })
59
+ ], ProfileMetadata.prototype, "value", 2);
60
+ ProfileMetadata = __decorateClass([
61
+ TenantScoped({ mode: "optional" }),
62
+ smrt({
63
+ tableStrategy: "sti",
64
+ api: { include: ["list", "get", "create", "update", "delete"] },
65
+ mcp: { include: ["list", "get"] },
66
+ cli: true
67
+ })
68
+ ], ProfileMetadata);
69
+ class ProfileMetadataCollection extends SmrtCollection {
70
+ static _itemClass = ProfileMetadata;
71
+ /**
72
+ * Get all metadata for a profile
73
+ *
74
+ * @param profileId - The profile UUID
75
+ * @returns Array of ProfileMetadata instances
76
+ */
77
+ async getByProfile(profileId) {
78
+ return await this.list({ where: { profileId } });
79
+ }
80
+ /**
81
+ * Get metadata as key-value object for a profile
82
+ *
83
+ * @param profileId - The profile UUID
84
+ * @returns Object with metafield slugs as keys
85
+ */
86
+ async getMetadataObject(profileId) {
87
+ const metadata = await this.getByProfile(profileId);
88
+ const result = {};
89
+ for (const item of metadata) {
90
+ const slug = await item.getMetafieldSlug();
91
+ if (slug) {
92
+ result[slug] = item.value;
93
+ }
94
+ }
95
+ return result;
96
+ }
97
+ /**
98
+ * Find all profiles with a specific metadata key-value pair
99
+ *
100
+ * @param metafieldId - The metafield UUID
101
+ * @param value - The value to match
102
+ * @returns Array of profile UUIDs
103
+ */
104
+ async findProfilesByMetadata(metafieldId, value) {
105
+ const matches = await this.list({
106
+ where: { metafieldId, value: String(value) }
107
+ });
108
+ return matches.map((m) => m.profileId).filter((id) => typeof id === "string");
109
+ }
110
+ }
111
+ const ProfileMetadataCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
112
+ __proto__: null,
113
+ ProfileMetadataCollection
114
+ }, Symbol.toStringTag, { value: "Module" }));
115
+ export {
116
+ ProfileMetadata as P,
117
+ ProfileMetadataCollection as a,
118
+ ProfileMetadataCollection$1 as b
119
+ };
120
+ //# sourceMappingURL=ProfileMetadataCollection-DEhmljMY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetadataCollection-DEhmljMY.js","sources":["../../src/models/ProfileMetadata.ts","../../src/collections/ProfileMetadataCollection.ts"],"sourcesContent":["/**\n * ProfileMetadata model - Stores metadata values for profiles\n *\n * Links profiles to metafield definitions with actual values.\n * Uses UUID primary key with foreign keys to Profile and ProfileMetafield.\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ProfileMetadataOptions extends SmrtObjectOptions {\n profileId?: string;\n metafieldId?: string;\n value?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ProfileMetadata extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n @foreignKey('Profile', { required: true })\n profileId?: string; // References Profile.id\n\n @foreignKey('ProfileMetafield', { required: true })\n metafieldId?: string; // References ProfileMetafield.id\n\n @field({ required: true })\n value: string = ''; // The metadata value (stored as text)\n\n constructor(options: ProfileMetadataOptions = {}) {\n super(options);\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.metafieldId !== undefined)\n this.metafieldId = options.metafieldId;\n if (options.value) this.value = options.value;\n }\n\n /**\n * Validate this metadata value against the metafield's validation schema\n *\n * @returns True if valid, throws error if invalid\n */\n async validate(): Promise<boolean> {\n const metafield = await this.loadRelated('metafieldId');\n if (!metafield) {\n throw new Error('Metafield not found');\n }\n\n return await metafield.validateValue(this.value);\n }\n\n /**\n * Get the metafield slug for this metadata\n *\n * @returns The slug of the metafield\n */\n async getMetafieldSlug(): Promise<string> {\n const metafield = await this.loadRelated('metafieldId');\n return metafield?.slug || '';\n }\n}\n","/**\n * ProfileMetadataCollection - Collection manager for ProfileMetadata objects\n *\n * Provides querying and batch operations for profile metadata.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProfileMetadata } from '../models/ProfileMetadata';\n\nexport class ProfileMetadataCollection extends SmrtCollection<ProfileMetadata> {\n static readonly _itemClass = ProfileMetadata;\n\n /**\n * Get all metadata for a profile\n *\n * @param profileId - The profile UUID\n * @returns Array of ProfileMetadata instances\n */\n async getByProfile(profileId: string): Promise<ProfileMetadata[]> {\n return await this.list({ where: { profileId } });\n }\n\n /**\n * Get metadata as key-value object for a profile\n *\n * @param profileId - The profile UUID\n * @returns Object with metafield slugs as keys\n */\n async getMetadataObject(profileId: string): Promise<Record<string, any>> {\n const metadata = await this.getByProfile(profileId);\n const result: Record<string, any> = {};\n\n for (const item of metadata) {\n const slug = await item.getMetafieldSlug();\n if (slug) {\n result[slug] = item.value;\n }\n }\n\n return result;\n }\n\n /**\n * Find all profiles with a specific metadata key-value pair\n *\n * @param metafieldId - The metafield UUID\n * @param value - The value to match\n * @returns Array of profile UUIDs\n */\n async findProfilesByMetadata(\n metafieldId: string,\n value: any,\n ): Promise<string[]> {\n const matches = await this.list({\n where: { metafieldId, value: String(value) },\n });\n\n return matches\n .map((m) => m.profileId)\n .filter((id) => typeof id === 'string') as string[];\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA8BO,IAAM,kBAAN,cAA8B,WAAW;AAAA,EAE9C,WAA0B;AAAA,EAI1B;AAAA,EAGA;AAAA,EAGA,QAAgB;AAAA;AAAA,EAEhB,YAAY,UAAkC,IAAI;AAChD,UAAM,OAAO;AACb,QAAI,QAAQ,cAAc,OAAW,MAAK,YAAY,QAAQ;AAC9D,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,cAAc,QAAQ;AAC7B,QAAI,QAAQ,MAAO,MAAK,QAAQ,QAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAA6B;AACjC,UAAM,YAAY,MAAM,KAAK,YAAY,aAAa;AACtD,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AAEA,WAAO,MAAM,UAAU,cAAc,KAAK,KAAK;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBAAoC;AACxC,UAAM,YAAY,MAAM,KAAK,YAAY,aAAa;AACtD,WAAO,WAAW,QAAQ;AAAA,EAC5B;AACF;AA3CE,gBAAA;AAAA,EADC,SAAS,EAAE,UAAU,KAAA,CAAM;AAAA,GADjB,gBAEX,WAAA,YAAA,CAAA;AAIA,gBAAA;AAAA,EADC,WAAW,WAAW,EAAE,UAAU,MAAM;AAAA,GAL9B,gBAMX,WAAA,aAAA,CAAA;AAGA,gBAAA;AAAA,EADC,WAAW,oBAAoB,EAAE,UAAU,MAAM;AAAA,GARvC,gBASX,WAAA,eAAA,CAAA;AAGA,gBAAA;AAAA,EADC,MAAM,EAAE,UAAU,KAAA,CAAM;AAAA,GAXd,gBAYX,WAAA,SAAA,CAAA;AAZW,kBAAN,gBAAA;AAAA,EAPN,aAAa,EAAE,MAAM,YAAY;AAAA,EACjC,KAAK;AAAA,IACJ,eAAe;AAAA,IACf,KAAK,EAAE,SAAS,CAAC,QAAQ,OAAO,UAAU,UAAU,QAAQ,EAAA;AAAA,IAC5D,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAA;AAAA,IAC9B,KAAK;AAAA,EAAA,CACN;AAAA,GACY,eAAA;ACrBN,MAAM,kCAAkC,eAAgC;AAAA,EAC7E,OAAgB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ7B,MAAM,aAAa,WAA+C;AAChE,WAAO,MAAM,KAAK,KAAK,EAAE,OAAO,EAAE,UAAA,GAAa;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBAAkB,WAAiD;AACvE,UAAM,WAAW,MAAM,KAAK,aAAa,SAAS;AAClD,UAAM,SAA8B,CAAA;AAEpC,eAAW,QAAQ,UAAU;AAC3B,YAAM,OAAO,MAAM,KAAK,iBAAA;AACxB,UAAI,MAAM;AACR,eAAO,IAAI,IAAI,KAAK;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,uBACJ,aACA,OACmB;AACnB,UAAM,UAAU,MAAM,KAAK,KAAK;AAAA,MAC9B,OAAO,EAAE,aAAa,OAAO,OAAO,KAAK,EAAA;AAAA,IAAE,CAC5C;AAED,WAAO,QACJ,IAAI,CAAC,MAAM,EAAE,SAAS,EACtB,OAAO,CAAC,OAAO,OAAO,OAAO,QAAQ;AAAA,EAC1C;AACF;;;;;"}
@@ -0,0 +1,184 @@
1
+ import { field, smrt, SmrtObject, SmrtCollection } from "@happyvertical/smrt-core";
2
+ import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
+ if (kind && result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ function getCustomValidators() {
14
+ if (!globalThis.__smrtProfileMetafieldValidators) {
15
+ globalThis.__smrtProfileMetafieldValidators = /* @__PURE__ */ new Map();
16
+ }
17
+ return globalThis.__smrtProfileMetafieldValidators;
18
+ }
19
+ let ProfileMetafield = class extends SmrtObject {
20
+ tenantId = null;
21
+ name = "";
22
+ description;
23
+ validation;
24
+ // Validation schema as JSON
25
+ constructor(options = {}) {
26
+ super(options);
27
+ if (options.name) this.name = options.name;
28
+ if (options.description !== void 0)
29
+ this.description = options.description;
30
+ if (options.validation !== void 0) this.validation = options.validation;
31
+ }
32
+ /**
33
+ * Convenience method for slug-based lookup
34
+ *
35
+ * @param slug - The slug to search for
36
+ * @returns ProfileMetafield instance or null if not found
37
+ */
38
+ static async getBySlug(_slug) {
39
+ return null;
40
+ }
41
+ /**
42
+ * Register a custom validator function
43
+ *
44
+ * @param name - Name of the validator (used in validation.custom field)
45
+ * @param validator - The validator function
46
+ */
47
+ static registerValidator(name, validator) {
48
+ getCustomValidators().set(name, validator);
49
+ }
50
+ /**
51
+ * Get a registered custom validator
52
+ *
53
+ * @param name - Name of the validator
54
+ * @returns The validator function or undefined
55
+ */
56
+ static getValidator(name) {
57
+ return getCustomValidators().get(name);
58
+ }
59
+ /**
60
+ * Validate a value against this metafield's validation schema
61
+ *
62
+ * @param value - The value to validate
63
+ * @returns True if valid, throws ValidationError if invalid
64
+ */
65
+ async validateValue(value) {
66
+ if (!this.validation) return true;
67
+ const schema = this.validation;
68
+ if (schema.type) {
69
+ const actualType = typeof value;
70
+ const expectedType = schema.type;
71
+ if (expectedType === "number" && actualType !== "number") {
72
+ throw new Error(
73
+ schema.message || `Expected ${expectedType}, got ${actualType}`
74
+ );
75
+ }
76
+ if (expectedType === "string" && actualType !== "string") {
77
+ throw new Error(
78
+ schema.message || `Expected ${expectedType}, got ${actualType}`
79
+ );
80
+ }
81
+ if (expectedType === "boolean" && actualType !== "boolean") {
82
+ throw new Error(
83
+ schema.message || `Expected ${expectedType}, got ${actualType}`
84
+ );
85
+ }
86
+ }
87
+ if (schema.pattern && typeof value === "string") {
88
+ const regex = new RegExp(schema.pattern);
89
+ if (!regex.test(value)) {
90
+ throw new Error(schema.message || "Pattern validation failed");
91
+ }
92
+ }
93
+ if (schema.minLength && typeof value === "string") {
94
+ if (value.length < schema.minLength) {
95
+ throw new Error(
96
+ schema.message || `Minimum length is ${schema.minLength}, got ${value.length}`
97
+ );
98
+ }
99
+ }
100
+ if (schema.maxLength && typeof value === "string") {
101
+ if (value.length > schema.maxLength) {
102
+ throw new Error(
103
+ schema.message || `Maximum length is ${schema.maxLength}, got ${value.length}`
104
+ );
105
+ }
106
+ }
107
+ if (schema.min !== void 0 && typeof value === "number") {
108
+ if (value < schema.min) {
109
+ throw new Error(
110
+ schema.message || `Value must be at least ${schema.min}`
111
+ );
112
+ }
113
+ }
114
+ if (schema.max !== void 0 && typeof value === "number") {
115
+ if (value > schema.max) {
116
+ throw new Error(
117
+ schema.message || `Value must be at most ${schema.max}`
118
+ );
119
+ }
120
+ }
121
+ if (schema.custom) {
122
+ const validator = ProfileMetafield.getValidator(schema.custom);
123
+ if (!validator) {
124
+ throw new Error(`Custom validator '${schema.custom}' not registered`);
125
+ }
126
+ const result = await validator(value);
127
+ if (!result) {
128
+ throw new Error(schema.message || "Custom validation failed");
129
+ }
130
+ }
131
+ return true;
132
+ }
133
+ };
134
+ __decorateClass([
135
+ tenantId({ nullable: true })
136
+ ], ProfileMetafield.prototype, "tenantId", 2);
137
+ __decorateClass([
138
+ field({ required: true })
139
+ ], ProfileMetafield.prototype, "name", 2);
140
+ ProfileMetafield = __decorateClass([
141
+ TenantScoped({ mode: "optional" }),
142
+ smrt({
143
+ tableStrategy: "sti",
144
+ api: { include: ["list", "get", "create", "update"] },
145
+ mcp: { include: ["list", "get"] },
146
+ cli: true
147
+ })
148
+ ], ProfileMetafield);
149
+ class ProfileMetafieldCollection extends SmrtCollection {
150
+ static _itemClass = ProfileMetafield;
151
+ /**
152
+ * Get metafield by slug
153
+ *
154
+ * @param slug - The slug to search for
155
+ * @returns ProfileMetafield instance or null
156
+ */
157
+ async getBySlug(slug) {
158
+ return await this.get({ slug });
159
+ }
160
+ /**
161
+ * Get or create a metafield by slug
162
+ *
163
+ * @param slug - The slug to search for
164
+ * @param defaults - Default values if creating
165
+ * @returns ProfileMetafield instance
166
+ */
167
+ async getOrCreateBySlug(slug, defaults) {
168
+ const existing = await this.getBySlug(slug);
169
+ if (existing) return existing;
170
+ const metafield = await this.create({ slug, ...defaults });
171
+ await metafield.save();
172
+ return metafield;
173
+ }
174
+ }
175
+ const ProfileMetafieldCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
176
+ __proto__: null,
177
+ ProfileMetafieldCollection
178
+ }, Symbol.toStringTag, { value: "Module" }));
179
+ export {
180
+ ProfileMetafield as P,
181
+ ProfileMetafieldCollection as a,
182
+ ProfileMetafieldCollection$1 as b
183
+ };
184
+ //# sourceMappingURL=ProfileMetafieldCollection-DMKhSHXX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetafieldCollection-DMKhSHXX.js","sources":["../../src/models/ProfileMetafield.ts","../../src/collections/ProfileMetafieldCollection.ts"],"sourcesContent":["/**\n * ProfileMetafield model - Controlled vocabulary for metadata fields\n *\n * Defines allowed metadata keys with validation rules.\n * Uses UUID primary key with unique slug for human-readable lookups.\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ValidationSchema, ValidatorFunction } from '../types';\n\nexport interface ProfileMetafieldOptions extends SmrtObjectOptions {\n slug?: string;\n name?: string;\n description?: string;\n validation?: ValidationSchema;\n tenantId?: string | null;\n}\n\n/**\n * Extend globalThis to include custom validators registry.\n * Using globalThis ensures all module instances share the same validators,\n * which is critical in monorepos where the same package can be loaded\n * from different paths (e.g., pnpm store vs workspace symlink).\n *\n * @see https://github.com/happyvertical/smrt/issues/543\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtProfileMetafieldValidators:\n | Map<string, ValidatorFunction>\n | undefined;\n}\n\n/**\n * Get the custom validators Map from globalThis\n */\nfunction getCustomValidators(): Map<string, ValidatorFunction> {\n if (!globalThis.__smrtProfileMetafieldValidators) {\n globalThis.__smrtProfileMetafieldValidators = new Map<\n string,\n ValidatorFunction\n >();\n }\n return globalThis.__smrtProfileMetafieldValidators;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ProfileMetafield extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n // slug is inherited from SmrtObject (auto-generated from name)\n @field({ required: true })\n name: string = '';\n\n description?: string;\n\n validation?: Record<string, any>; // Validation schema as JSON\n\n constructor(options: ProfileMetafieldOptions = {}) {\n super(options);\n if (options.name) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.validation !== undefined) this.validation = options.validation;\n }\n\n /**\n * Convenience method for slug-based lookup\n *\n * @param slug - The slug to search for\n * @returns ProfileMetafield instance or null if not found\n */\n static async getBySlug(_slug: string): Promise<ProfileMetafield | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n\n /**\n * Register a custom validator function\n *\n * @param name - Name of the validator (used in validation.custom field)\n * @param validator - The validator function\n */\n static registerValidator(name: string, validator: ValidatorFunction): void {\n getCustomValidators().set(name, validator);\n }\n\n /**\n * Get a registered custom validator\n *\n * @param name - Name of the validator\n * @returns The validator function or undefined\n */\n static getValidator(name: string): ValidatorFunction | undefined {\n return getCustomValidators().get(name);\n }\n\n /**\n * Validate a value against this metafield's validation schema\n *\n * @param value - The value to validate\n * @returns True if valid, throws ValidationError if invalid\n */\n async validateValue(value: any): Promise<boolean> {\n if (!this.validation) return true;\n\n const schema = this.validation as ValidationSchema;\n\n // Type validation\n if (schema.type) {\n const actualType = typeof value;\n const expectedType = schema.type;\n\n if (expectedType === 'number' && actualType !== 'number') {\n throw new Error(\n schema.message || `Expected ${expectedType}, got ${actualType}`,\n );\n }\n if (expectedType === 'string' && actualType !== 'string') {\n throw new Error(\n schema.message || `Expected ${expectedType}, got ${actualType}`,\n );\n }\n if (expectedType === 'boolean' && actualType !== 'boolean') {\n throw new Error(\n schema.message || `Expected ${expectedType}, got ${actualType}`,\n );\n }\n }\n\n // String validations\n if (schema.pattern && typeof value === 'string') {\n const regex = new RegExp(schema.pattern);\n if (!regex.test(value)) {\n throw new Error(schema.message || 'Pattern validation failed');\n }\n }\n\n if (schema.minLength && typeof value === 'string') {\n if (value.length < schema.minLength) {\n throw new Error(\n schema.message ||\n `Minimum length is ${schema.minLength}, got ${value.length}`,\n );\n }\n }\n\n if (schema.maxLength && typeof value === 'string') {\n if (value.length > schema.maxLength) {\n throw new Error(\n schema.message ||\n `Maximum length is ${schema.maxLength}, got ${value.length}`,\n );\n }\n }\n\n // Numeric validations\n if (schema.min !== undefined && typeof value === 'number') {\n if (value < schema.min) {\n throw new Error(\n schema.message || `Value must be at least ${schema.min}`,\n );\n }\n }\n\n if (schema.max !== undefined && typeof value === 'number') {\n if (value > schema.max) {\n throw new Error(\n schema.message || `Value must be at most ${schema.max}`,\n );\n }\n }\n\n // Custom validator\n if (schema.custom) {\n const validator = ProfileMetafield.getValidator(schema.custom);\n if (!validator) {\n throw new Error(`Custom validator '${schema.custom}' not registered`);\n }\n\n const result = await validator(value);\n if (!result) {\n throw new Error(schema.message || 'Custom validation failed');\n }\n }\n\n return true;\n }\n}\n","/**\n * ProfileMetafieldCollection - Collection manager for ProfileMetafield objects\n *\n * Provides querying for profile metafield lookup table.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProfileMetafield } from '../models/ProfileMetafield';\nimport type { ValidationSchema } from '../types';\n\nexport class ProfileMetafieldCollection extends SmrtCollection<ProfileMetafield> {\n static readonly _itemClass = ProfileMetafield;\n\n /**\n * Get metafield by slug\n *\n * @param slug - The slug to search for\n * @returns ProfileMetafield instance or null\n */\n async getBySlug(slug: string): Promise<ProfileMetafield | null> {\n return await this.get({ slug });\n }\n\n /**\n * Get or create a metafield by slug\n *\n * @param slug - The slug to search for\n * @param defaults - Default values if creating\n * @returns ProfileMetafield instance\n */\n async getOrCreateBySlug(\n slug: string,\n defaults: {\n name: string;\n description?: string;\n validation?: ValidationSchema;\n },\n ): Promise<ProfileMetafield> {\n const existing = await this.getBySlug(slug);\n if (existing) return existing;\n\n const metafield = await this.create({ slug, ...defaults });\n await metafield.save();\n return metafield;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ import { foreignKey, oneToMany, smrt, SmrtObject, SmrtCollection } from "@happyvertical/smrt-core";
2
+ import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
+ if (kind && result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ let ProfileRelationship = class extends SmrtObject {
14
+ tenantId = null;
15
+ fromProfileId;
16
+ toProfileId;
17
+ typeId;
18
+ contextProfileId;
19
+ terms = [];
20
+ constructor(options = {}) {
21
+ super(options);
22
+ if (options.fromProfileId !== void 0)
23
+ this.fromProfileId = options.fromProfileId;
24
+ if (options.toProfileId !== void 0)
25
+ this.toProfileId = options.toProfileId;
26
+ if (options.typeId !== void 0) this.typeId = options.typeId;
27
+ if (options.contextProfileId !== void 0)
28
+ this.contextProfileId = options.contextProfileId;
29
+ }
30
+ /**
31
+ * Get the relationship type slug
32
+ *
33
+ * @returns The slug of the relationship type
34
+ */
35
+ async getTypeSlug() {
36
+ const type = await this.loadRelated("typeId");
37
+ return type?.slug || "";
38
+ }
39
+ /**
40
+ * Add a term (time period) to this relationship
41
+ *
42
+ * @param startedAt - Start date of the term
43
+ * @param endedAt - Optional end date of the term
44
+ */
45
+ async addTerm(startedAt, endedAt) {
46
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-CXem_qT-.js").then((n) => n.b);
47
+ const termCollection = await ProfileRelationshipTermCollection.create(this.options);
48
+ const term = await termCollection.create({
49
+ relationshipId: this.id,
50
+ startedAt,
51
+ endedAt
52
+ });
53
+ await term.save();
54
+ }
55
+ /**
56
+ * End the current active term
57
+ *
58
+ * @param endedAt - End date for the term
59
+ */
60
+ async endCurrentTerm(endedAt) {
61
+ const activeTerm = await this.getActiveTerm();
62
+ if (activeTerm) {
63
+ await activeTerm.end(endedAt);
64
+ }
65
+ }
66
+ /**
67
+ * Get all terms for this relationship
68
+ *
69
+ * @returns Array of ProfileRelationshipTerm instances
70
+ */
71
+ async getTerms() {
72
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-CXem_qT-.js").then((n) => n.b);
73
+ const termCollection = await ProfileRelationshipTermCollection.create(this.options);
74
+ return await termCollection.getByRelationship(this.id);
75
+ }
76
+ /**
77
+ * Get the active term (no end date)
78
+ *
79
+ * @returns Current term or null if none active
80
+ */
81
+ async getActiveTerm() {
82
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-CXem_qT-.js").then((n) => n.b);
83
+ const termCollection = await ProfileRelationshipTermCollection.create(this.options);
84
+ return await termCollection.getActiveTerm(this.id);
85
+ }
86
+ };
87
+ __decorateClass([
88
+ tenantId({ nullable: true })
89
+ ], ProfileRelationship.prototype, "tenantId", 2);
90
+ __decorateClass([
91
+ foreignKey("Profile", { required: true })
92
+ ], ProfileRelationship.prototype, "fromProfileId", 2);
93
+ __decorateClass([
94
+ foreignKey("Profile", { required: true })
95
+ ], ProfileRelationship.prototype, "toProfileId", 2);
96
+ __decorateClass([
97
+ foreignKey("ProfileRelationshipType", { required: true })
98
+ ], ProfileRelationship.prototype, "typeId", 2);
99
+ __decorateClass([
100
+ foreignKey("Profile")
101
+ ], ProfileRelationship.prototype, "contextProfileId", 2);
102
+ __decorateClass([
103
+ oneToMany("ProfileRelationshipTerm")
104
+ ], ProfileRelationship.prototype, "terms", 2);
105
+ ProfileRelationship = __decorateClass([
106
+ TenantScoped({ mode: "optional" }),
107
+ smrt({
108
+ tableStrategy: "sti",
109
+ api: { include: ["list", "get", "create", "delete"] },
110
+ mcp: { include: ["list", "get"] },
111
+ cli: true
112
+ })
113
+ ], ProfileRelationship);
114
+ class ProfileRelationshipCollection extends SmrtCollection {
115
+ static _itemClass = ProfileRelationship;
116
+ /**
117
+ * Get all relationships from a profile
118
+ *
119
+ * @param fromProfileId - The origin profile UUID
120
+ * @param typeId - Optional filter by relationship type UUID
121
+ * @returns Array of ProfileRelationship instances
122
+ */
123
+ async getFromProfile(fromProfileId, typeId) {
124
+ const where = { fromProfileId };
125
+ if (typeId) where.typeId = typeId;
126
+ return await this.list({ where });
127
+ }
128
+ /**
129
+ * Get all relationships to a profile
130
+ *
131
+ * @param toProfileId - The target profile UUID
132
+ * @param typeId - Optional filter by relationship type UUID
133
+ * @returns Array of ProfileRelationship instances
134
+ */
135
+ async getToProfile(toProfileId, typeId) {
136
+ const where = { toProfileId };
137
+ if (typeId) where.typeId = typeId;
138
+ return await this.list({ where });
139
+ }
140
+ /**
141
+ * Get all relationships for a profile (both directions)
142
+ *
143
+ * @param profileId - The profile UUID
144
+ * @param typeId - Optional filter by relationship type UUID
145
+ * @returns Array of ProfileRelationship instances
146
+ */
147
+ async getForProfile(profileId, typeId) {
148
+ const fromRelationships = await this.getFromProfile(profileId, typeId);
149
+ const toRelationships = await this.getToProfile(profileId, typeId);
150
+ return [...fromRelationships, ...toRelationships];
151
+ }
152
+ /**
153
+ * Check if a relationship exists between two profiles
154
+ *
155
+ * @param fromProfileId - The origin profile UUID
156
+ * @param toProfileId - The target profile UUID
157
+ * @param typeId - The relationship type UUID
158
+ * @returns True if relationship exists
159
+ */
160
+ async exists(fromProfileId, toProfileId, typeId) {
161
+ const matches = await this.list({
162
+ where: { fromProfileId, toProfileId, typeId },
163
+ limit: 1
164
+ });
165
+ return matches.length > 0;
166
+ }
167
+ }
168
+ const ProfileRelationshipCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
169
+ __proto__: null,
170
+ ProfileRelationshipCollection
171
+ }, Symbol.toStringTag, { value: "Module" }));
172
+ export {
173
+ ProfileRelationship as P,
174
+ ProfileRelationshipCollection as a,
175
+ ProfileRelationshipCollection$1 as b
176
+ };
177
+ //# sourceMappingURL=ProfileRelationshipCollection-C0IM8UQR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileRelationshipCollection-C0IM8UQR.js","sources":["../../src/models/ProfileRelationship.ts","../../src/collections/ProfileRelationshipCollection.ts"],"sourcesContent":["/**\n * ProfileRelationship model - Connects two profiles with a relationship type\n *\n * Represents directional or reciprocal relationships between profiles.\n * Uses UUID primary key with foreign keys to profiles and relationship type.\n */\n\nimport {\n foreignKey,\n oneToMany,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ProfileRelationshipTerm } from './ProfileRelationshipTerm';\n\nexport interface ProfileRelationshipOptions extends SmrtObjectOptions {\n fromProfileId?: string;\n toProfileId?: string;\n typeId?: string;\n contextProfileId?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ProfileRelationship extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n @foreignKey('Profile', { required: true })\n fromProfileId?: string; // Origin profile\n\n @foreignKey('Profile', { required: true })\n toProfileId?: string; // Target profile\n\n @foreignKey('ProfileRelationshipType', { required: true })\n typeId?: string; // Relationship type\n\n @foreignKey('Profile')\n contextProfileId?: string; // Optional tertiary context\n\n // Relationships\n @oneToMany('ProfileRelationshipTerm')\n terms: any[] = [];\n\n constructor(options: ProfileRelationshipOptions = {}) {\n super(options);\n if (options.fromProfileId !== undefined)\n this.fromProfileId = options.fromProfileId;\n if (options.toProfileId !== undefined)\n this.toProfileId = options.toProfileId;\n if (options.typeId !== undefined) this.typeId = options.typeId;\n if (options.contextProfileId !== undefined)\n this.contextProfileId = options.contextProfileId;\n }\n\n /**\n * Get the relationship type slug\n *\n * @returns The slug of the relationship type\n */\n async getTypeSlug(): Promise<string> {\n const type = await this.loadRelated('typeId');\n return type?.slug || '';\n }\n\n /**\n * Add a term (time period) to this relationship\n *\n * @param startedAt - Start date of the term\n * @param endedAt - Optional end date of the term\n */\n async addTerm(startedAt: Date, endedAt?: Date): Promise<void> {\n const { ProfileRelationshipTermCollection } = await import(\n '../collections/ProfileRelationshipTermCollection'\n );\n\n const termCollection = await (\n ProfileRelationshipTermCollection as any\n ).create(this.options);\n\n const term = await termCollection.create({\n relationshipId: this.id,\n startedAt,\n endedAt,\n });\n\n await term.save();\n }\n\n /**\n * End the current active term\n *\n * @param endedAt - End date for the term\n */\n async endCurrentTerm(endedAt: Date): Promise<void> {\n const activeTerm = await this.getActiveTerm();\n if (activeTerm) {\n await activeTerm.end(endedAt);\n }\n }\n\n /**\n * Get all terms for this relationship\n *\n * @returns Array of ProfileRelationshipTerm instances\n */\n async getTerms(): Promise<ProfileRelationshipTerm[]> {\n const { ProfileRelationshipTermCollection } = await import(\n '../collections/ProfileRelationshipTermCollection'\n );\n\n const termCollection = await (\n ProfileRelationshipTermCollection as any\n ).create(this.options);\n\n return await termCollection.getByRelationship(this.id);\n }\n\n /**\n * Get the active term (no end date)\n *\n * @returns Current term or null if none active\n */\n async getActiveTerm(): Promise<ProfileRelationshipTerm | null> {\n const { ProfileRelationshipTermCollection } = await import(\n '../collections/ProfileRelationshipTermCollection'\n );\n\n const termCollection = await (\n ProfileRelationshipTermCollection as any\n ).create(this.options);\n\n return await termCollection.getActiveTerm(this.id);\n }\n}\n","/**\n * ProfileRelationshipCollection - Collection manager for ProfileRelationship objects\n *\n * Provides querying for relationships between profiles.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProfileRelationship } from '../models/ProfileRelationship';\n\nexport class ProfileRelationshipCollection extends SmrtCollection<ProfileRelationship> {\n static readonly _itemClass = ProfileRelationship;\n\n /**\n * Get all relationships from a profile\n *\n * @param fromProfileId - The origin profile UUID\n * @param typeId - Optional filter by relationship type UUID\n * @returns Array of ProfileRelationship instances\n */\n async getFromProfile(\n fromProfileId: string,\n typeId?: string,\n ): Promise<ProfileRelationship[]> {\n const where: any = { fromProfileId };\n if (typeId) where.typeId = typeId;\n\n return await this.list({ where });\n }\n\n /**\n * Get all relationships to a profile\n *\n * @param toProfileId - The target profile UUID\n * @param typeId - Optional filter by relationship type UUID\n * @returns Array of ProfileRelationship instances\n */\n async getToProfile(\n toProfileId: string,\n typeId?: string,\n ): Promise<ProfileRelationship[]> {\n const where: any = { toProfileId };\n if (typeId) where.typeId = typeId;\n\n return await this.list({ where });\n }\n\n /**\n * Get all relationships for a profile (both directions)\n *\n * @param profileId - The profile UUID\n * @param typeId - Optional filter by relationship type UUID\n * @returns Array of ProfileRelationship instances\n */\n async getForProfile(\n profileId: string,\n typeId?: string,\n ): Promise<ProfileRelationship[]> {\n const fromRelationships = await this.getFromProfile(profileId, typeId);\n const toRelationships = await this.getToProfile(profileId, typeId);\n\n return [...fromRelationships, ...toRelationships];\n }\n\n /**\n * Check if a relationship exists between two profiles\n *\n * @param fromProfileId - The origin profile UUID\n * @param toProfileId - The target profile UUID\n * @param typeId - The relationship type UUID\n * @returns True if relationship exists\n */\n async exists(\n fromProfileId: string,\n toProfileId: string,\n typeId: string,\n ): Promise<boolean> {\n const matches = await this.list({\n where: { fromProfileId, toProfileId, typeId },\n limit: 1,\n });\n\n return matches.length > 0;\n }\n}\n"],"names":[],"mappings":"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"}