{"version":3,"file":"name-resolver.mjs","names":["InjectableScope","InstanceNameCache","cache","Map","maxSize","get","key","value","undefined","delete","set","has","size","firstKey","keys","next","clear","hashArgs","args","str","JSON","stringify","Object","sort","hash","i","length","char","charCodeAt","Math","abs","toString","NameResolver","instanceNameCache","logger","generateInstanceName","token","requestId","scope","tokenStr","isRequest","Request","Error","cacheKey","cached","result","argsHash","upgradeInstanceNameToRequest","existingName","includes","requestIdPattern","hasRequestId","test","colonIndex","indexOf","tokenPart","substring","argsPart","startsWith","formatArgValue","name","slice"],"sources":["../../../../src/internal/core/name-resolver.mts"],"sourcesContent":["import type { InjectionTokenType } from '../../token/injection-token.mjs'\n\nimport { InjectableScope } from '../../enums/index.mjs'\n\n/**\n * Simple LRU cache for instance name generation.\n * Uses a Map which maintains insertion order for efficient LRU eviction.\n */\nclass InstanceNameCache {\n  private readonly cache = new Map<string, string>()\n  private readonly maxSize: number\n\n  constructor(maxSize = 1000) {\n    this.maxSize = maxSize\n  }\n\n  get(key: string): string | undefined {\n    const value = this.cache.get(key)\n    if (value !== undefined) {\n      // Move to end (most recently used)\n      this.cache.delete(key)\n      this.cache.set(key, value)\n    }\n    return value\n  }\n\n  set(key: string, value: string): void {\n    if (this.cache.has(key)) {\n      this.cache.delete(key)\n    } else if (this.cache.size >= this.maxSize) {\n      // Remove least recently used (first item)\n      const firstKey = this.cache.keys().next().value\n      if (firstKey !== undefined) {\n        this.cache.delete(firstKey)\n      }\n    }\n    this.cache.set(key, value)\n  }\n\n  clear(): void {\n    this.cache.clear()\n  }\n}\n\n/**\n * Simple hash function for deterministic hashing of arguments\n */\nfunction hashArgs(args: any): string {\n  const str = JSON.stringify(args, Object.keys(args || {}).sort())\n  let hash = 0\n  for (let i = 0; i < str.length; i++) {\n    const char = str.charCodeAt(i)\n    hash = (hash << 5) - hash + char\n    hash = hash & hash // Convert to 32-bit integer\n  }\n  return Math.abs(hash).toString(36)\n}\n\n/**\n * Handles instance name generation with support for requestId and scope.\n *\n * Generates unique instance identifiers based on token, arguments, and scope.\n * Request-scoped services MUST include requestId in their name for proper isolation.\n */\nexport class NameResolver {\n  private readonly instanceNameCache = new InstanceNameCache()\n\n  constructor(private readonly logger: Console | null = null) {}\n\n  /**\n   * Generates a unique instance name based on token, arguments, requestId, and scope.\n   *\n   * Name formats:\n   * - Singleton/Transient without args: `${tokenId}`\n   * - Singleton/Transient with args: `${tokenId}:${argsHash}`\n   * - Request without args: `${tokenId}:requestId=${requestId}`\n   * - Request with args: `${tokenId}:requestId=${requestId}:${argsHash}`\n   *\n   * @param token The injection token\n   * @param args Optional arguments\n   * @param requestId Optional request ID (required for request-scoped services)\n   * @param scope Optional scope (used to determine if requestId should be included)\n   * @returns The generated instance name\n   */\n  generateInstanceName(\n    token: InjectionTokenType,\n    args?: any,\n    requestId?: string,\n    scope?: InjectableScope,\n  ): string {\n    const tokenStr = token.toString()\n    const isRequest = scope === InjectableScope.Request\n\n    // For request-scoped services, requestId is required\n    if (isRequest && !requestId) {\n      throw new Error(\n        `[NameResolver] requestId is required for request-scoped services`,\n      )\n    }\n\n    // Build cache key\n    const cacheKey = `${tokenStr}:${scope}:${requestId || ''}:${args ? JSON.stringify(args) : ''}`\n\n    // Check cache first\n    const cached = this.instanceNameCache.get(cacheKey)\n    if (cached !== undefined) {\n      return cached\n    }\n\n    // Generate the instance name\n    let result = tokenStr\n\n    // Add requestId for request-scoped services\n    if (isRequest && requestId) {\n      result = `${result}:requestId=${requestId}`\n    }\n\n    // Add args hash if args are provided\n    if (args) {\n      const argsHash = hashArgs(args)\n      result = `${result}:${argsHash}`\n    }\n\n    // Cache the result\n    this.instanceNameCache.set(cacheKey, result)\n\n    return result\n  }\n\n  /**\n   * Upgrades an existing instance name to include requestId.\n   * Preserves any args hash that might already be in the name.\n   *\n   * Examples:\n   * - `TokenName` → `TokenName:requestId=req-123`\n   * - `TokenName:abc123` → `TokenName:requestId=req-123:abc123`\n   *\n   * @param existingName The existing instance name (without requestId)\n   * @param requestId The request ID to add\n   * @returns The upgraded instance name with requestId\n   */\n  upgradeInstanceNameToRequest(\n    existingName: string,\n    requestId: string,\n  ): string {\n    // Check if requestId is already in the name\n    if (existingName.includes(`:requestId=${requestId}`)) {\n      return existingName\n    }\n\n    // Find where to insert requestId\n    // Format: TokenName or TokenName:argsHash\n    // We want: TokenName:requestId=req-123 or TokenName:requestId=req-123:argsHash\n\n    // Check if there's an args hash (starts after first colon, but not requestId=)\n    const requestIdPattern = /:requestId=/\n    const hasRequestId = requestIdPattern.test(existingName)\n\n    if (hasRequestId) {\n      // Already has a requestId, don't upgrade\n      return existingName\n    }\n\n    // Find the token part (everything before first colon, or entire string if no colon)\n    const colonIndex = existingName.indexOf(':')\n    if (colonIndex === -1) {\n      // No colon, just token name: TokenName → TokenName:requestId=req-123\n      return `${existingName}:requestId=${requestId}`\n    }\n\n    // Has colon, means there's an args hash: TokenName:abc123 → TokenName:requestId=req-123:abc123\n    const tokenPart = existingName.substring(0, colonIndex)\n    const argsPart = existingName.substring(colonIndex + 1)\n\n    // Check if argsPart looks like an args hash (not requestId=)\n    if (argsPart.startsWith('requestId=')) {\n      // Already has requestId, return as is\n      return existingName\n    }\n\n    return `${tokenPart}:requestId=${requestId}:${argsPart}`\n  }\n\n  /**\n   * Formats a single argument value for instance name generation.\n   */\n  formatArgValue(value: any): string {\n    if (typeof value === 'function') {\n      return `fn_${value.name}(${value.length})`\n    }\n    if (typeof value === 'symbol') {\n      return value.toString()\n    }\n    return JSON.stringify(value).slice(0, 40)\n  }\n}\n"],"mappings":"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"}