@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":"index-jFtOWsAV.js","sources":["../../src/__smrt-register__.ts","../../src/models/MagicLinkToken.ts","../../src/collections/MagicLinkTokenCollection.ts","../../src/auth/magicLinkService.ts","../../src/auth/nip05Handler.ts","../../src/models/OidcIdentity.ts","../../src/auth/resolveIdentity.ts","../../src/collections/OidcIdentityCollection.ts","../../src/collections/ProfileTypeCollection.ts","../../src/models/ProfileTypes.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * MagicLinkToken - Temporary tokens for email verification\n *\n * Stores hashed magic link tokens with expiration. Tokens are single-use\n * and automatically invalidated after verification or expiration.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { NostrIdentity } from './NostrIdentity';\n\nexport interface MagicLinkTokenOptions extends SmrtObjectOptions {\n nostrIdentityId?: string;\n tokenHash?: string;\n email?: string;\n expiresAt?: Date;\n usedAt?: Date | null;\n requestedFromIp?: string;\n createdAt?: Date;\n}\n\nexport interface GenerateTokenResult {\n /** The plaintext token (only available at creation time) */\n token: string;\n /** The MagicLinkToken record */\n magicLinkToken: MagicLinkToken;\n}\n\n/** Default token expiration: 15 minutes */\nconst DEFAULT_EXPIRATION_MINUTES = 15;\n\n@smrt({\n tableName: 'magic_link_tokens',\n api: { exclude: ['*'] }, // No API access - internal only\n mcp: { exclude: ['*'] },\n cli: { include: ['list'] }, // Admin visibility only\n})\nexport class MagicLinkToken extends SmrtObject {\n /**\n * Link to the NostrIdentity this token is for\n */\n @foreignKey('NostrIdentity', { required: true })\n nostrIdentityId?: string;\n\n /**\n * SHA-256 hash of the token (never store plaintext)\n */\n tokenHash: string = '';\n\n /**\n * Email this token was sent to\n */\n email: string = '';\n\n /**\n * When this token expires\n */\n expiresAt: Date = new Date(\n Date.now() + DEFAULT_EXPIRATION_MINUTES * 60 * 1000,\n );\n\n /**\n * When this token was used (null = unused)\n */\n usedAt: Date | null = null;\n\n /**\n * IP address that requested the token (for rate limiting)\n */\n requestedFromIp: string = '';\n\n /**\n * When this token was created (for rate limiting)\n */\n createdAt: Date = new Date();\n\n constructor(options: MagicLinkTokenOptions = {}) {\n super(options);\n if (options.nostrIdentityId) this.nostrIdentityId = options.nostrIdentityId;\n if (options.tokenHash) this.tokenHash = options.tokenHash;\n if (options.email) this.email = options.email;\n if (options.expiresAt) this.expiresAt = options.expiresAt;\n if (options.usedAt !== undefined) this.usedAt = options.usedAt;\n if (options.requestedFromIp) this.requestedFromIp = options.requestedFromIp;\n if (options.createdAt) this.createdAt = options.createdAt;\n }\n\n /**\n * Hash a token using SHA-256\n */\n static hashToken(token: string): string {\n return createHash('sha256').update(token).digest('hex');\n }\n\n /**\n * Generate a new magic link token\n * @param nostrIdentity - The identity this token is for\n * @param email - The email address to send to\n * @param options - Additional options\n */\n static async generate(\n nostrIdentity: NostrIdentity,\n email: string,\n options: {\n expiresInMinutes?: number;\n requestedFromIp?: string;\n db?: SmrtObjectOptions['db'];\n } = {},\n ): Promise<GenerateTokenResult> {\n // Generate 32 bytes of random data (256 bits of entropy)\n const tokenBytes = randomBytes(32);\n const token = tokenBytes.toString('base64url');\n const tokenHash = MagicLinkToken.hashToken(token);\n\n const expiresInMinutes =\n options.expiresInMinutes ?? DEFAULT_EXPIRATION_MINUTES;\n const expiresAt = new Date(Date.now() + expiresInMinutes * 60 * 1000);\n\n const magicLinkToken = new MagicLinkToken({\n db: options.db ?? nostrIdentity.options?.db,\n nostrIdentityId: nostrIdentity.id as string,\n tokenHash,\n email: email.toLowerCase(),\n expiresAt,\n requestedFromIp: options.requestedFromIp || '',\n });\n\n await magicLinkToken.initialize();\n await magicLinkToken.save();\n\n return { token, magicLinkToken };\n }\n\n /**\n * Verify a token and return the MagicLinkToken if valid\n * @param token - The plaintext token to verify\n * @param options - Database options\n * @returns The MagicLinkToken if valid, null otherwise\n */\n static async verify(\n token: string,\n options: SmrtObjectOptions = {},\n ): Promise<MagicLinkToken | null> {\n const tokenHash = MagicLinkToken.hashToken(token);\n\n const { MagicLinkTokenCollection } = await import(\n '../collections/MagicLinkTokenCollection'\n );\n const collection = await (MagicLinkTokenCollection as any).create(options);\n\n const magicLinkToken = await collection.findOne({\n where: { tokenHash },\n });\n\n if (!magicLinkToken) {\n return null;\n }\n\n // Check if valid\n if (!magicLinkToken.isValid()) {\n return null;\n }\n\n return magicLinkToken;\n }\n\n /**\n * Check if this token is valid (not expired and not used)\n */\n isValid(): boolean {\n if (this.usedAt !== null) {\n return false;\n }\n if (new Date() > this.expiresAt) {\n return false;\n }\n return true;\n }\n\n /**\n * Check if this token has expired\n */\n isExpired(): boolean {\n return new Date() > this.expiresAt;\n }\n\n /**\n * Check if this token has been used\n */\n isUsed(): boolean {\n return this.usedAt !== null;\n }\n\n /**\n * Mark this token as used\n */\n async markUsed(): Promise<void> {\n this.usedAt = new Date();\n await this.save();\n }\n\n /**\n * Get the NostrIdentity this token is for\n */\n async getNostrIdentity(): Promise<NostrIdentity | null> {\n return (await this.getRelated('nostrIdentityId')) as NostrIdentity | null;\n }\n\n /**\n * Get time remaining until expiration in seconds\n */\n getTimeRemainingSeconds(): number {\n const now = new Date();\n const diff = this.expiresAt.getTime() - now.getTime();\n return Math.max(0, Math.floor(diff / 1000));\n }\n}\n","/**\n * MagicLinkTokenCollection - Collection for managing magic link tokens\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { MagicLinkToken } from '../models/MagicLinkToken';\n\nexport class MagicLinkTokenCollection extends SmrtCollection<MagicLinkToken> {\n static readonly _itemClass = MagicLinkToken;\n\n /**\n * Find token by hash\n */\n async findByTokenHash(tokenHash: string): Promise<MagicLinkToken | null> {\n return await this.findOne({\n where: { tokenHash },\n });\n }\n\n /**\n * Find active (non-expired, non-used) tokens for an email\n */\n async findActiveForEmail(email: string): Promise<MagicLinkToken[]> {\n const tokens = await this.list({\n where: { email: email.toLowerCase() },\n });\n return tokens.filter((token) => token.isValid());\n }\n\n /**\n * Find tokens for a NostrIdentity\n */\n async findByIdentity(nostrIdentityId: string): Promise<MagicLinkToken[]> {\n return await this.list({\n where: { nostrIdentityId },\n });\n }\n\n /**\n * Count tokens created for an email within a time window (for rate limiting)\n * @param email - Email address\n * @param withinMinutes - Time window in minutes\n */\n async countRecentByEmail(\n email: string,\n withinMinutes: number,\n ): Promise<number> {\n const cutoff = new Date(Date.now() - withinMinutes * 60 * 1000);\n const tokens = await this.list({\n where: { email: email.toLowerCase() },\n });\n\n // Filter tokens created after cutoff\n // Note: We use createdAt from SmrtObject base\n return tokens.filter((token) => {\n const createdAt = (token as any).createdAt;\n return createdAt && new Date(createdAt) > cutoff;\n }).length;\n }\n\n /**\n * Count tokens created from an IP within a time window (for rate limiting)\n * @param ip - IP address\n * @param withinMinutes - Time window in minutes\n */\n async countRecentByIp(ip: string, withinMinutes: number): Promise<number> {\n const cutoff = new Date(Date.now() - withinMinutes * 60 * 1000);\n const tokens = await this.list({\n where: { requestedFromIp: ip },\n });\n\n // Filter tokens created after cutoff\n return tokens.filter((token) => {\n const createdAt = (token as any).createdAt;\n return createdAt && new Date(createdAt) > cutoff;\n }).length;\n }\n\n /**\n * Verify a token and return it if valid\n */\n async verify(token: string): Promise<MagicLinkToken | null> {\n const tokenHash = MagicLinkToken.hashToken(token);\n const magicLinkToken = await this.findByTokenHash(tokenHash);\n\n if (!magicLinkToken) {\n return null;\n }\n\n if (!magicLinkToken.isValid()) {\n return null;\n }\n\n return magicLinkToken;\n }\n\n /**\n * Clean up expired tokens by deleting them\n * @returns Number of tokens deleted\n */\n async cleanupExpired(): Promise<number> {\n const now = new Date();\n const allTokens = await this.list({});\n let deleted = 0;\n\n for (const token of allTokens) {\n if (token.isExpired() || token.isUsed()) {\n await token.delete();\n deleted++;\n }\n }\n\n return deleted;\n }\n\n /**\n * Revoke all tokens for a NostrIdentity (e.g., when identity is deleted)\n */\n async revokeForIdentity(nostrIdentityId: string): Promise<number> {\n const tokens = await this.findByIdentity(nostrIdentityId);\n let revoked = 0;\n\n for (const token of tokens) {\n if (!token.isUsed()) {\n await token.markUsed(); // Mark as used to invalidate\n revoked++;\n }\n }\n\n return revoked;\n }\n\n /**\n * Check if rate limit is exceeded for email\n * @param email - Email address\n * @param maxPerHour - Maximum tokens allowed per hour (default: 5)\n */\n async isRateLimitedByEmail(\n email: string,\n maxPerHour: number = 5,\n ): Promise<boolean> {\n const count = await this.countRecentByEmail(email, 60);\n return count >= maxPerHour;\n }\n\n /**\n * Check if rate limit is exceeded for IP\n * @param ip - IP address\n * @param maxPerHour - Maximum tokens allowed per hour (default: 10)\n */\n async isRateLimitedByIp(\n ip: string,\n maxPerHour: number = 10,\n ): Promise<boolean> {\n const count = await this.countRecentByIp(ip, 60);\n return count >= maxPerHour;\n }\n}\n","/**\n * Magic Link Service\n *\n * Handles the magic link authentication flow including:\n * - Token generation and storage\n * - Email sending (via callback)\n * - Token verification\n * - Rate limiting checks\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { MagicLinkTokenCollection } from '../collections/MagicLinkTokenCollection';\nimport { NostrIdentityCollection } from '../collections/NostrIdentityCollection';\nimport { MagicLinkToken } from '../models/MagicLinkToken';\nimport type { NostrIdentity } from '../models/NostrIdentity';\nimport type { Profile } from '../models/Profile';\nimport { encryptPrivkey, generateNostrKeypair } from './nostrCrypto';\n\nexport interface MagicLinkConfig {\n /** Base URL for magic links (e.g., \"https://example.com\") */\n baseUrl: string;\n /** Path for verification endpoint (default: \"/auth/verify\") */\n verifyPath?: string;\n /** Token expiration in minutes (default: 15) */\n expiresInMinutes?: number;\n /** Maximum tokens per email per hour (default: 5) */\n maxTokensPerEmailPerHour?: number;\n /** Maximum tokens per IP per hour (default: 10) */\n maxTokensPerIpPerHour?: number;\n /** Server master secret for encryption */\n masterSecret: string;\n /** Callback to send the email */\n sendEmail: (to: string, magicLink: string) => Promise<void>;\n /** Database options */\n db: SmrtObjectOptions['db'];\n}\n\nexport interface InitiateResult {\n success: boolean;\n /** The Nostr identity (existing or new) */\n nostrIdentity?: NostrIdentity;\n /** The profile (existing or new) */\n profile?: Profile;\n /** True if a new profile was created */\n created: boolean;\n /** Error message if success is false */\n error?: string;\n /** Error code for programmatic handling */\n errorCode?: 'RATE_LIMITED_EMAIL' | 'RATE_LIMITED_IP' | 'EMAIL_SEND_FAILED';\n}\n\nexport interface VerifyResult {\n success: boolean;\n /** The profile */\n profile?: Profile;\n /** The Nostr identity */\n nostrIdentity?: NostrIdentity;\n /** The decrypted keypair (only on success) */\n keypair?: {\n pubkey: string;\n privkey: string;\n npub: string;\n nsec: string;\n };\n /** Error message if success is false */\n error?: string;\n /** Error code for programmatic handling */\n errorCode?: 'INVALID_TOKEN' | 'EXPIRED_TOKEN' | 'USED_TOKEN' | 'NOT_FOUND';\n}\n\nexport interface MagicLinkService {\n /**\n * Initiate magic link flow - generates token and sends email\n */\n initiate(\n email: string,\n options?: {\n requestedFromIp?: string;\n nip05Username?: string;\n },\n ): Promise<InitiateResult>;\n\n /**\n * Verify a magic link token and return decrypted keypair\n */\n verify(token: string): Promise<VerifyResult>;\n}\n\n/**\n * Create a magic link service instance\n */\nexport function createMagicLinkService(\n config: MagicLinkConfig,\n): MagicLinkService {\n const {\n baseUrl,\n verifyPath = '/auth/verify',\n expiresInMinutes = 15,\n maxTokensPerEmailPerHour = 5,\n maxTokensPerIpPerHour = 10,\n masterSecret,\n sendEmail,\n db,\n } = config;\n\n return {\n async initiate(\n email: string,\n options: {\n requestedFromIp?: string;\n nip05Username?: string;\n } = {},\n ): Promise<InitiateResult> {\n const normalizedEmail = email.toLowerCase().trim();\n const { requestedFromIp, nip05Username } = options;\n\n // Create collections\n const identityCollection = await NostrIdentityCollection.create({ db });\n const tokenCollection = await MagicLinkTokenCollection.create({ db });\n\n // Rate limiting checks\n if (\n await tokenCollection.isRateLimitedByEmail(\n normalizedEmail,\n maxTokensPerEmailPerHour,\n )\n ) {\n return {\n success: false,\n created: false,\n error: 'Too many requests for this email. Please try again later.',\n errorCode: 'RATE_LIMITED_EMAIL',\n };\n }\n\n if (\n requestedFromIp &&\n (await tokenCollection.isRateLimitedByIp(\n requestedFromIp,\n maxTokensPerIpPerHour,\n ))\n ) {\n return {\n success: false,\n created: false,\n error: 'Too many requests from this IP. Please try again later.',\n errorCode: 'RATE_LIMITED_IP',\n };\n }\n\n // Check if identity exists\n let nostrIdentity = await identityCollection.findByEmail(normalizedEmail);\n let profile: Profile | null = null;\n let created = false;\n\n if (!nostrIdentity) {\n // Create new identity and profile\n const { Person } = await import('../models/ProfileTypes');\n\n // Create a new profile for this user (Person is an STI subclass of Profile)\n profile = new Person({\n db,\n email: normalizedEmail,\n name: normalizedEmail.split('@')[0], // Default name from email\n });\n await profile.initialize();\n await profile.save();\n\n // Generate keypair and encrypt\n const keypair = generateNostrKeypair();\n const encrypted = encryptPrivkey(keypair.privkey, masterSecret);\n\n // Create Nostr identity\n const { NostrIdentity: NostrIdentityClass } = await import(\n '../models/NostrIdentity'\n );\n nostrIdentity = new NostrIdentityClass({\n db,\n profileId: profile.id!,\n pubkey: keypair.pubkey,\n encryptedPrivkey: encrypted.ciphertext,\n encryptionIv: encrypted.iv,\n encryptionTag: encrypted.tag,\n email: normalizedEmail,\n nip05Username:\n nip05Username?.toLowerCase() || normalizedEmail.split('@')[0],\n });\n await nostrIdentity.initialize();\n await nostrIdentity.save();\n\n created = true;\n } else {\n profile = await nostrIdentity.getProfile();\n }\n\n // Generate magic link token\n const { token } = await MagicLinkToken.generate(\n nostrIdentity,\n normalizedEmail,\n {\n expiresInMinutes,\n requestedFromIp,\n db,\n },\n );\n\n // Build magic link URL\n const magicLink = `${baseUrl}${verifyPath}?token=${encodeURIComponent(token)}`;\n\n // Send email\n try {\n await sendEmail(normalizedEmail, magicLink);\n } catch (emailError) {\n return {\n success: false,\n created,\n nostrIdentity,\n profile: profile || undefined,\n error: 'Failed to send email. Please try again.',\n errorCode: 'EMAIL_SEND_FAILED',\n };\n }\n\n return {\n success: true,\n created,\n nostrIdentity,\n profile: profile || undefined,\n };\n },\n\n async verify(token: string): Promise<VerifyResult> {\n // Verify token\n const magicLinkToken = await MagicLinkToken.verify(token, { db });\n\n if (!magicLinkToken) {\n // Try to get more specific error\n const tokenHash = MagicLinkToken.hashToken(token);\n const tokenCollection = await MagicLinkTokenCollection.create({ db });\n const existingToken = await tokenCollection.findByTokenHash(tokenHash);\n\n if (!existingToken) {\n return {\n success: false,\n error: 'Invalid or expired token.',\n errorCode: 'NOT_FOUND',\n };\n }\n\n if (existingToken.isUsed()) {\n return {\n success: false,\n error: 'This link has already been used.',\n errorCode: 'USED_TOKEN',\n };\n }\n\n if (existingToken.isExpired()) {\n return {\n success: false,\n error: 'This link has expired. Please request a new one.',\n errorCode: 'EXPIRED_TOKEN',\n };\n }\n\n return {\n success: false,\n error: 'Invalid token.',\n errorCode: 'INVALID_TOKEN',\n };\n }\n\n // Mark token as used\n await magicLinkToken.markUsed();\n\n // Get the Nostr identity\n const nostrIdentity = await magicLinkToken.getNostrIdentity();\n if (!nostrIdentity) {\n return {\n success: false,\n error: 'Identity not found.',\n errorCode: 'NOT_FOUND',\n };\n }\n\n // Mark identity as verified\n await nostrIdentity.markVerified();\n\n // Get profile\n const profile = await nostrIdentity.getProfile();\n\n // Decrypt keypair\n const keypair = nostrIdentity.getKeypair(masterSecret);\n\n return {\n success: true,\n profile: profile || undefined,\n nostrIdentity,\n keypair,\n };\n },\n };\n}\n","/**\n * NIP-05 Handler\n *\n * Provides a helper for serving the /.well-known/nostr.json endpoint\n * for NIP-05 identity verification.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/05.md\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type Nip05Response,\n NostrIdentityCollection,\n} from '../collections/NostrIdentityCollection';\n\nexport interface Nip05HandlerConfig {\n /** Database options */\n db: SmrtObjectOptions['db'];\n /** Optional: Additional relays to include for all users */\n defaultRelays?: string[];\n /** Optional: Cache TTL in seconds (default: 300 = 5 minutes) */\n cacheTtlSeconds?: number;\n}\n\nexport interface Nip05Request {\n /** The 'name' query parameter from the request */\n name?: string | null;\n}\n\nexport interface Nip05HandlerResult {\n /** The NIP-05 response body */\n body: Nip05Response;\n /** Suggested headers for the response */\n headers: Record<string, string>;\n /** HTTP status code */\n status: number;\n}\n\n/**\n * Create a NIP-05 handler\n *\n * @example\n * // SvelteKit\n * import { createNip05Handler } from '@happyvertical/smrt-profiles';\n *\n * const handler = createNip05Handler({ db: { type: 'postgres', url: DATABASE_URL } });\n *\n * export async function GET({ url }) {\n * const result = await handler({ name: url.searchParams.get('name') });\n * return new Response(JSON.stringify(result.body), {\n * status: result.status,\n * headers: result.headers,\n * });\n * }\n *\n * @example\n * // Express\n * import { createNip05Handler } from '@happyvertical/smrt-profiles';\n *\n * const handler = createNip05Handler({ db: { type: 'postgres', url: DATABASE_URL } });\n *\n * app.get('/.well-known/nostr.json', async (req, res) => {\n * const result = await handler({ name: req.query.name });\n * res.status(result.status).set(result.headers).json(result.body);\n * });\n */\nexport function createNip05Handler(config: Nip05HandlerConfig) {\n const { db, defaultRelays = [], cacheTtlSeconds = 300 } = config;\n\n return async function handleNip05Request(\n request: Nip05Request,\n ): Promise<Nip05HandlerResult> {\n const { name } = request;\n\n const collection = await NostrIdentityCollection.create({ db });\n const response = await collection.getNip05Response(name || undefined);\n\n // Add default relays if configured\n if (defaultRelays.length > 0 && Object.keys(response.names).length > 0) {\n response.relays = {};\n for (const pubkey of Object.values(response.names)) {\n response.relays[pubkey] = defaultRelays;\n }\n }\n\n // If specific name requested but not found, return empty names\n if (name && Object.keys(response.names).length === 0) {\n return {\n body: { names: {} },\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n 'Cache-Control': `public, max-age=${cacheTtlSeconds}`,\n },\n status: 200, // NIP-05 spec says return 200 with empty names, not 404\n };\n }\n\n return {\n body: response,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n 'Cache-Control': `public, max-age=${cacheTtlSeconds}`,\n },\n status: 200,\n };\n };\n}\n\n/**\n * Validate a NIP-05 identifier format\n * @param identifier - The identifier to validate (e.g., \"alice@example.com\")\n */\nexport function isValidNip05Identifier(identifier: string): boolean {\n // NIP-05 format: <local-part>@<domain>\n const parts = identifier.split('@');\n if (parts.length !== 2) {\n return false;\n }\n\n const [localPart, domain] = parts;\n\n // Local part: alphanumeric, _, -\n if (!/^[a-z0-9_-]+$/i.test(localPart)) {\n return false;\n }\n\n // Domain: basic domain validation\n if (!/^[a-z0-9.-]+\\.[a-z]{2,}$/i.test(domain)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Parse a NIP-05 identifier into its parts\n * @param identifier - The identifier to parse (e.g., \"alice@example.com\")\n */\nexport function parseNip05Identifier(identifier: string): {\n localPart: string;\n domain: string;\n} | null {\n if (!isValidNip05Identifier(identifier)) {\n return null;\n }\n\n const [localPart, domain] = identifier.split('@');\n return { localPart, domain };\n}\n","/**\n * OidcIdentity - Links OIDC provider identities to Profile\n *\n * Stores the mapping between external OIDC providers (Keycloak, Google, GitHub)\n * and internal Profile records. Multiple identities can link to a single profile.\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Profile } from './Profile';\n\nexport interface OidcIdentityOptions extends SmrtObjectOptions {\n profileId?: string;\n provider?: string;\n issuer?: string;\n subject?: string;\n email?: string;\n lastUsedAt?: Date | null;\n}\n\n@smrt({\n tableName: 'oidc_identities',\n api: { exclude: ['delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class OidcIdentity extends SmrtObject {\n /**\n * Link to the Profile (Person, Organization, Bot)\n */\n @foreignKey('Profile', { required: true })\n profileId?: string;\n\n /**\n * Provider name (e.g., 'keycloak', 'google', 'github')\n */\n @field({ type: 'text' })\n provider: string = '';\n\n /**\n * OIDC issuer URL (e.g., https://keycloak.example.com/realms/bmp)\n */\n @field({ type: 'text' })\n issuer: string = '';\n\n /**\n * OIDC subject claim - unique identifier from the provider\n */\n @field({ type: 'text' })\n subject: string = '';\n\n /**\n * Cached email from the IdP (for display/lookup)\n */\n @field({ type: 'text' })\n email: string = '';\n\n /**\n * Last time this identity was used for authentication\n */\n @field({ type: 'datetime', nullable: true })\n lastUsedAt: Date | null = null;\n\n constructor(options: OidcIdentityOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.provider) this.provider = options.provider;\n if (options.issuer) this.issuer = options.issuer;\n if (options.subject) this.subject = options.subject;\n if (options.email) this.email = options.email;\n if (options.lastUsedAt !== undefined) this.lastUsedAt = options.lastUsedAt;\n }\n\n /**\n * Get the linked Profile\n */\n async getProfile(): Promise<Profile | null> {\n return (await this.getRelated('profileId')) as Profile | null;\n }\n\n /**\n * Find identity by issuer and subject\n */\n static async findBySubject(\n issuer: string,\n subject: string,\n options: SmrtObjectOptions = {},\n ): Promise<OidcIdentity | null> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await (OidcIdentityCollection as any).create(options);\n return await collection.findOne({\n where: { issuer, subject },\n });\n }\n\n /**\n * Find or create identity for a profile\n */\n static async findOrCreate(\n profile: Profile,\n oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n },\n options: SmrtObjectOptions = {},\n ): Promise<OidcIdentity> {\n const existing = await OidcIdentity.findBySubject(\n oidcData.issuer,\n oidcData.subject,\n options,\n );\n\n if (existing) {\n // Update last used\n existing.lastUsedAt = new Date();\n if (oidcData.email) existing.email = oidcData.email;\n await existing.save();\n return existing;\n }\n\n // Create new\n const identity = new OidcIdentity({\n ...options,\n profileId: profile.id as string,\n provider: oidcData.provider,\n issuer: oidcData.issuer,\n subject: oidcData.subject,\n email: oidcData.email || '',\n });\n await identity.initialize();\n await identity.save();\n return identity;\n }\n\n /**\n * Record usage of this identity\n */\n async recordUsage(): Promise<void> {\n this.lastUsedAt = new Date();\n await this.save();\n }\n}\n","/**\n * resolveIdentity - Resolves authentication context to a Profile\n *\n * Used by apps (blindmanpress.com) to resolve incoming auth to a Profile.\n * Modules (aedile, praeco) should receive the resolved profile from the app.\n *\n * Resolution order:\n * 1. API key header → ApiKey → Profile\n * 2. OIDC session → OidcIdentity → Profile\n * 3. Nostr auth → NostrIdentity → Profile\n * 4. Actor header (CI pass-through) → Profile lookup\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { ApiKey } from '../models/ApiKey';\nimport { NostrIdentity } from '../models/NostrIdentity';\nimport { OidcIdentity } from '../models/OidcIdentity';\nimport type { Profile } from '../models/Profile';\nimport { type NostrEvent, verifyAuthEvent } from './nostrCrypto';\n\n/**\n * Context provided to resolveIdentity\n */\nexport interface AuthContext {\n /**\n * API key from X-API-Key header or similar\n */\n apiKey?: string | null;\n\n /**\n * OIDC session data (from @auth/sveltekit or similar)\n */\n oidcSession?: {\n sub?: string;\n iss?: string;\n email?: string;\n name?: string;\n } | null;\n\n /**\n * Actor identifier for CI pass-through identity\n * Usually the GitHub actor (username) who triggered the workflow\n */\n actor?: string | null;\n\n /**\n * Nostr authentication data (NIP-42 style)\n */\n nostrAuth?: {\n /** Signed Nostr event for authentication */\n event: NostrEvent;\n /** Expected challenge (to prevent replay attacks) */\n challenge: string;\n } | null;\n\n /**\n * Database/persistence options\n */\n db?: SmrtObjectOptions['db'];\n}\n\n/**\n * Result of identity resolution\n */\nexport interface ResolveIdentityResult {\n /**\n * The resolved profile, or null if not authenticated\n */\n profile: Profile | null;\n\n /**\n * How the identity was resolved\n */\n source: 'api_key' | 'oidc' | 'nostr' | 'actor' | 'none';\n\n /**\n * The API key record if authenticated via API key\n */\n apiKey?: ApiKey;\n\n /**\n * The OIDC identity record if authenticated via OIDC\n */\n oidcIdentity?: OidcIdentity;\n\n /**\n * The Nostr identity record if authenticated via Nostr\n */\n nostrIdentity?: NostrIdentity;\n}\n\n/**\n * Resolve authentication context to a Profile\n *\n * @param context - The authentication context from the request\n * @returns The resolved profile and metadata\n *\n * @example\n * ```typescript\n * // In SvelteKit hooks.server.ts\n * import { resolveIdentity } from '@happyvertical/smrt-profiles';\n *\n * const identityMiddleware: Handle = async ({ event, resolve }) => {\n * const session = await event.locals.auth();\n *\n * const { profile, source } = await resolveIdentity({\n * oidcSession: session,\n * apiKey: event.request.headers.get('X-API-Key'),\n * actor: event.request.headers.get('X-Actor'),\n * db: { type: 'postgres', url: DATABASE_URL },\n * });\n *\n * event.locals.profile = profile;\n * event.locals.authSource = source;\n *\n * return resolve(event);\n * };\n * ```\n */\nexport async function resolveIdentity(\n context: AuthContext,\n): Promise<ResolveIdentityResult> {\n const options: SmrtObjectOptions = { db: context.db };\n\n // 1. Check API key header first (highest priority for programmatic access)\n if (context.apiKey) {\n const apiKey = await ApiKey.verify(context.apiKey, options);\n if (apiKey) {\n const profile = await apiKey.getProfile();\n if (profile) {\n return {\n profile,\n source: 'api_key',\n apiKey,\n };\n }\n }\n }\n\n // 2. Check OIDC session (web users)\n if (context.oidcSession?.sub && context.oidcSession?.iss) {\n const oidcIdentity = await OidcIdentity.findBySubject(\n context.oidcSession.iss,\n context.oidcSession.sub,\n options,\n );\n\n if (oidcIdentity) {\n // Record usage\n await oidcIdentity.recordUsage();\n\n const profile = await oidcIdentity.getProfile();\n if (profile) {\n return {\n profile,\n source: 'oidc',\n oidcIdentity,\n };\n }\n }\n }\n\n // 3. Check Nostr auth (NIP-42 style signed event)\n if (context.nostrAuth?.event && context.nostrAuth?.challenge) {\n const { event, challenge } = context.nostrAuth;\n\n // Verify the auth event\n const verifyResult = verifyAuthEvent(event, challenge);\n if (verifyResult.valid) {\n // Look up identity by public key\n const nostrIdentity = await NostrIdentity.findByPubkey(\n event.pubkey,\n options,\n );\n\n if (nostrIdentity) {\n // Record usage\n await nostrIdentity.recordUsage();\n\n const profile = await nostrIdentity.getProfile();\n if (profile) {\n return {\n profile,\n source: 'nostr',\n nostrIdentity,\n };\n }\n }\n }\n }\n\n // 4. Check actor for CI pass-through (look up by metadata)\n if (context.actor) {\n const profile = await findProfileByExternalId(\n 'github',\n context.actor,\n options,\n );\n if (profile) {\n return {\n profile,\n source: 'actor',\n };\n }\n }\n\n // No authentication found\n return {\n profile: null,\n source: 'none',\n };\n}\n\n/**\n * Find a profile by external ID (e.g., GitHub username)\n *\n * This looks up profiles by their linked external identities or metadata.\n *\n * @param provider - The external provider (e.g., 'github')\n * @param externalId - The external identifier (e.g., GitHub username)\n * @param options - Database options\n * @returns The profile or null\n */\nasync function findProfileByExternalId(\n provider: string,\n externalId: string,\n options: SmrtObjectOptions,\n): Promise<Profile | null> {\n // First check OIDC identities (if they use the provider)\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n\n const oidcCollection = await (OidcIdentityCollection as any).create(options);\n const identities = await oidcCollection.findByProvider(provider);\n\n for (const identity of identities) {\n // Check if the subject matches the external ID\n if (\n identity.subject === externalId ||\n identity.email?.includes(externalId)\n ) {\n const profile = await identity.getProfile();\n if (profile) return profile;\n }\n }\n\n // Could also check profile metadata for external IDs\n // This would require a standardized metadata field like 'github_username'\n const { ProfileCollection } = await import(\n '../collections/ProfileCollection'\n );\n const profileCollection = await (ProfileCollection as any).create(options);\n\n // Try to find by email that looks like a GitHub noreply\n const profiles = await profileCollection.list({\n where: {\n email: `${externalId}@users.noreply.github.com`,\n },\n limit: 1,\n });\n\n if (profiles.length > 0) {\n return profiles[0];\n }\n\n return null;\n}\n\n/**\n * Create a profile from OIDC claims if it doesn't exist\n *\n * Supports email-based account linking: if a user signs in with Google\n * and later with GitHub using the same email, they get the same profile.\n *\n * Resolution order:\n * 1. If OIDC identity (iss + sub) already exists → return linked profile\n * 2. If verified email provided, check if profile with same email exists → link new identity\n * 3. Otherwise, create new profile + identity\n *\n * Security considerations:\n * - Email-based linking only occurs when `email_verified` is true. This prevents\n * attackers from claiming unverified emails to hijack accounts.\n * - Linking is automatic and irreversible through this API. Multiple OIDC\n * identities from different providers sharing the same verified email will\n * be associated to the same Profile.\n * - If an OIDC provider does not supply an email, or the email changes later,\n * existing links are not automatically updated. New sign-ins without an email\n * or with a different email may result in a new Profile being created.\n * - This function trusts the OIDC provider to assert correct email_verified status.\n * Only use with trusted providers.\n *\n * @param claims - OIDC token claims\n * @param provider - Provider name (e.g., 'keycloak', 'google', 'github')\n * @param options - Database options\n * @returns The created or existing profile with linked OIDC identity\n */\nexport async function createProfileFromOidc(\n claims: {\n sub: string;\n iss: string;\n email?: string;\n email_verified?: boolean;\n name?: string;\n preferred_username?: string;\n },\n provider: string,\n options: SmrtObjectOptions,\n): Promise<{ profile: Profile; oidcIdentity: OidcIdentity; created: boolean }> {\n // 1. If OIDC identity already exists for this issuer+subject, return linked profile\n const existingIdentity = await OidcIdentity.findBySubject(\n claims.iss,\n claims.sub,\n options,\n );\n\n if (existingIdentity) {\n const profile = await existingIdentity.getProfile();\n if (profile) {\n // Update identity with latest claims\n if (claims.email) existingIdentity.email = claims.email;\n existingIdentity.lastUsedAt = new Date();\n await existingIdentity.save();\n\n return {\n profile,\n oidcIdentity: existingIdentity,\n created: false,\n };\n }\n }\n\n // 2. Email-based account linking: only link if email is verified (security)\n if (claims.email && claims.email_verified) {\n const { ProfileCollection } = await import(\n '../collections/ProfileCollection'\n );\n\n const profileCollection = await (ProfileCollection as any).create(options);\n const existingProfile = await profileCollection.findByEmail(claims.email);\n\n if (existingProfile) {\n // Link new OIDC identity to existing profile (email-based linking)\n const oidcIdentity = await OidcIdentity.findOrCreate(\n existingProfile,\n {\n provider,\n issuer: claims.iss,\n subject: claims.sub,\n email: claims.email,\n },\n options,\n );\n\n return {\n profile: existingProfile,\n oidcIdentity,\n created: false,\n };\n }\n }\n\n // 3. Create new profile\n const { Person } = await import('../models/ProfileTypes');\n const { ProfileTypeCollection } = await import(\n '../collections/ProfileTypeCollection'\n );\n\n // Get or create the 'person' type\n const typeCollection = await (ProfileTypeCollection as any).create(options);\n let personType = await typeCollection.getBySlug('person');\n\n if (!personType) {\n // Create the person type if it doesn't exist\n const { ProfileType } = await import('../models/ProfileType');\n personType = new ProfileType({\n ...options,\n slug: 'person',\n name: 'Person',\n description: 'Individual person profile',\n });\n await personType.initialize();\n await personType.save();\n }\n\n const profile = new Person({\n ...options,\n typeId: personType.id as string,\n email: claims.email || '',\n name: claims.name || claims.preferred_username || claims.sub,\n });\n await profile.initialize();\n await profile.save();\n\n // Link OIDC identity\n const oidcIdentity = await OidcIdentity.findOrCreate(\n profile,\n {\n provider,\n issuer: claims.iss,\n subject: claims.sub,\n email: claims.email,\n },\n options,\n );\n\n return {\n profile,\n oidcIdentity,\n created: true,\n };\n}\n\n/**\n * Create a profile from Nostr identity (used by magic link service)\n *\n * This is typically called internally by the magic link service,\n * but can be used directly if needed.\n *\n * @param email - Email address for the profile\n * @param nostrData - Encrypted Nostr keypair data\n * @param options - Database options\n * @returns The created profile with linked Nostr identity\n */\nexport async function createProfileFromNostr(\n email: string,\n nostrData: {\n pubkey: string;\n encryptedPrivkey: string;\n encryptionIv: string;\n encryptionTag: string;\n nip05Username?: string;\n },\n options: SmrtObjectOptions,\n): Promise<{\n profile: Profile;\n nostrIdentity: NostrIdentity;\n created: boolean;\n}> {\n const normalizedEmail = email.toLowerCase();\n\n // Check if Nostr identity already exists\n const existingIdentity = await NostrIdentity.findByEmail(\n normalizedEmail,\n options,\n );\n\n if (existingIdentity) {\n const profile = await existingIdentity.getProfile();\n if (profile) {\n return {\n profile,\n nostrIdentity: existingIdentity,\n created: false,\n };\n }\n }\n\n // Create new profile\n const { Person } = await import('../models/ProfileTypes');\n const { ProfileTypeCollection } = await import(\n '../collections/ProfileTypeCollection'\n );\n\n // Get or create the 'person' type\n const typeCollection = await (ProfileTypeCollection as any).create(options);\n let personType = await typeCollection.getBySlug('person');\n\n if (!personType) {\n const { ProfileType } = await import('../models/ProfileType');\n personType = new ProfileType({\n ...options,\n slug: 'person',\n name: 'Person',\n description: 'Individual person profile',\n });\n await personType.initialize();\n await personType.save();\n }\n\n const profile = new Person({\n ...options,\n typeId: personType.id as string,\n email: normalizedEmail,\n name: normalizedEmail.split('@')[0], // Default name from email\n });\n await profile.initialize();\n await profile.save();\n\n // Create Nostr identity\n const nostrIdentity = new NostrIdentity({\n ...options,\n profileId: profile.id as string,\n pubkey: nostrData.pubkey,\n encryptedPrivkey: nostrData.encryptedPrivkey,\n encryptionIv: nostrData.encryptionIv,\n encryptionTag: nostrData.encryptionTag,\n email: normalizedEmail,\n nip05Username:\n nostrData.nip05Username?.toLowerCase() || normalizedEmail.split('@')[0],\n });\n await nostrIdentity.initialize();\n await nostrIdentity.save();\n\n return {\n profile,\n nostrIdentity,\n created: true,\n };\n}\n","/**\n * OidcIdentityCollection - Collection for managing OIDC identity records\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { OidcIdentity } from '../models/OidcIdentity';\nimport type { Profile } from '../models/Profile';\n\nexport class OidcIdentityCollection extends SmrtCollection<OidcIdentity> {\n static readonly _itemClass = OidcIdentity;\n\n /**\n * Find identities for a profile\n */\n async findByProfile(profileId: string): Promise<OidcIdentity[]> {\n return await this.list({\n where: { profileId },\n });\n }\n\n /**\n * Find identity by issuer and subject\n */\n async findBySubject(\n issuer: string,\n subject: string,\n ): Promise<OidcIdentity | null> {\n return await this.findOne({\n where: { issuer, subject },\n });\n }\n\n /**\n * Find identities by provider\n */\n async findByProvider(provider: string): Promise<OidcIdentity[]> {\n return await this.list({\n where: { provider },\n });\n }\n\n /**\n * Link a new OIDC identity to a profile\n */\n async linkToProfile(\n profile: Profile,\n oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n },\n ): Promise<OidcIdentity> {\n // Check if already exists\n const existing = await this.findBySubject(\n oidcData.issuer,\n oidcData.subject,\n );\n if (existing) {\n // Update and return existing\n existing.lastUsedAt = new Date();\n if (oidcData.email) existing.email = oidcData.email;\n await existing.save();\n return existing;\n }\n\n // Create new\n const identity = new OidcIdentity({\n ...this.options,\n profileId: profile.id as string,\n provider: oidcData.provider,\n issuer: oidcData.issuer,\n subject: oidcData.subject,\n email: oidcData.email || '',\n });\n await identity.initialize();\n await identity.save();\n return identity;\n }\n\n /**\n * Unlink an OIDC identity from a profile\n */\n async unlinkFromProfile(\n profileId: string,\n issuer: string,\n subject: string,\n ): Promise<boolean> {\n const identity = await this.findOne({\n where: { profileId, issuer, subject },\n });\n\n if (identity) {\n await identity.delete();\n return true;\n }\n return false;\n }\n}\n","/**\n * ProfileTypeCollection - Collection manager for ProfileType objects\n *\n * Provides querying for profile type lookup table.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProfileType } from '../models/ProfileType';\n\nexport class ProfileTypeCollection extends SmrtCollection<ProfileType> {\n static readonly _itemClass = ProfileType;\n\n /**\n * Get profile type by slug\n *\n * @param slug - The slug to search for\n * @returns ProfileType instance or null\n */\n async getBySlug(slug: string): Promise<ProfileType | null> {\n return await this.get({ slug });\n }\n\n /**\n * Get or create a profile type by slug\n *\n * @param slug - The slug to search for\n * @param defaults - Default values if creating\n * @returns ProfileType instance\n */\n async getOrCreateBySlug(\n slug: string,\n defaults: { name: string; description?: string },\n ): Promise<ProfileType> {\n const existing = await this.getBySlug(slug);\n if (existing) return existing;\n\n const profileType = await this.create({ slug, ...defaults });\n await profileType.save();\n return profileType;\n }\n}\n","/**\n * Profile subclasses for STI (Single Table Inheritance)\n *\n * These classes inherit from Profile and automatically use the shared\n * profiles table with _meta_type discriminator.\n */\n\nimport { smrt } from '@happyvertical/smrt-core';\nimport { Profile, type ProfileOptions } from './Profile';\n\n/**\n * Person profile type\n *\n * Represents individual people/users.\n */\n@smrt({\n tableStrategy: 'sti',\n})\nexport class Person extends Profile {\n constructor(options: ProfileOptions = {}) {\n super(options);\n // Person-specific initialization can go here\n }\n}\n\n/**\n * Organization profile type\n *\n * Represents companies, groups, institutions, and other organizational entities.\n */\n@smrt({\n tableStrategy: 'sti',\n})\nexport class Organization extends Profile {\n constructor(options: ProfileOptions = {}) {\n super(options);\n // Organization-specific initialization can go here\n }\n}\n\n/**\n * Bot profile type\n *\n * Represents automated agents, bots, and AI entities.\n */\n@smrt({\n tableStrategy: 'sti',\n})\nexport class Bot extends Profile {\n constructor(options: ProfileOptions = {}) {\n super(options);\n // Bot-specific initialization can go here\n }\n}\n"],"names":["MagicLinkTokenCollection","__decorateClass","Person","OidcIdentityCollection","profile","oidcIdentity","ProfileTypeCollection","ProfileType"],"mappings":"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"}