nickname.ts raw

   1  /**
   2   * Value object representing a user-defined nickname for an identity.
   3   * Self-validating and immutable.
   4   */
   5  export class Nickname {
   6    private static readonly MAX_LENGTH = 50;
   7    private static readonly MIN_LENGTH = 1;
   8  
   9    private constructor(private readonly _value: string) {}
  10  
  11    /**
  12     * Create a new Nickname from a string value.
  13     * Trims whitespace and validates length.
  14     *
  15     * @throws Error if nickname is empty or too long
  16     */
  17    static create(value: string): Nickname {
  18      const trimmed = value?.trim() ?? '';
  19  
  20      if (trimmed.length < Nickname.MIN_LENGTH) {
  21        throw new InvalidNicknameError('Nickname cannot be empty');
  22      }
  23  
  24      if (trimmed.length > Nickname.MAX_LENGTH) {
  25        throw new InvalidNicknameError(
  26          `Nickname cannot exceed ${Nickname.MAX_LENGTH} characters`
  27        );
  28      }
  29  
  30      return new Nickname(trimmed);
  31    }
  32  
  33    /**
  34     * Reconstitute a Nickname from storage without validation.
  35     * Use only when loading from trusted storage.
  36     */
  37    static fromStorage(value: string): Nickname {
  38      return new Nickname(value);
  39    }
  40  
  41    get value(): string {
  42      return this._value;
  43    }
  44  
  45    equals(other: Nickname): boolean {
  46      return this._value === other._value;
  47    }
  48  
  49    toString(): string {
  50      return this._value;
  51    }
  52  
  53    toJSON(): string {
  54      return this._value;
  55    }
  56  }
  57  
  58  /**
  59   * Error thrown when nickname validation fails.
  60   */
  61  export class InvalidNicknameError extends Error {
  62    constructor(message: string) {
  63      super(message);
  64      this.name = 'InvalidNicknameError';
  65    }
  66  }
  67