{"version":3,"file":"ThresholdSessionRecord.mjs","names":[],"sources":["../../src/repository/ThresholdSessionRecord.ts"],"sourcesContent":["import type { TagsBase, RecordTags } from '@credo-ts/core'\n\nimport { BaseRecord, utils } from '@credo-ts/core'\n\nexport type ThresholdSessionStatus = 'pending' | 'complete' | 'expired' | 'failed'\n\nexport interface CollectedShare {\n  /** Share index (1-based) */\n  identifier: number\n  /** Base64url-encoded share data */\n  data: string\n  /** DID of the participant who provided this share */\n  providedBy: string\n  /** When the share was provided */\n  providedAt: Date\n}\n\nexport type CustomThresholdSessionTags = TagsBase\n\nexport type DefaultThresholdSessionTags = {\n  sessionId: string\n  vaultId: string\n  docId: string\n  status: ThresholdSessionStatus\n  initiatedBy: string\n}\n\nexport type ThresholdSessionTags = RecordTags<ThresholdSessionRecord>\n\nexport interface ThresholdSessionRecordProps {\n  id?: string\n  createdAt?: Date\n  updatedAt?: Date\n  tags?: CustomThresholdSessionTags\n\n  // Core identifiers\n  sessionId: string\n  vaultId: string\n  docId: string\n\n  // Threshold config\n  threshold: number // t (minimum shares needed)\n  totalShares: number // n (total shares)\n\n  // State\n  status: ThresholdSessionStatus\n  shares: CollectedShare[]\n  reconstructedCek?: string // base64url - only set when threshold met\n\n  // Metadata\n  initiatedBy: string // DID of session initiator\n  expiresAt?: Date\n  purpose?: string\n}\n\n/**\n * Database record for a threshold share collection session\n *\n * Used to track the collection of Shamir secret shares\n * for threshold (t-of-n) vault decryption\n */\nexport class ThresholdSessionRecord extends BaseRecord<DefaultThresholdSessionTags, CustomThresholdSessionTags> {\n  /** Unique session identifier */\n  public sessionId!: string\n\n  /** Associated vault ID */\n  public vaultId!: string\n\n  /** Associated document ID */\n  public docId!: string\n\n  /** Minimum shares required (t) */\n  public threshold!: number\n\n  /** Total shares (n) */\n  public totalShares!: number\n\n  /** Current session status */\n  public status!: ThresholdSessionStatus\n\n  /** Collected shares so far */\n  public shares!: CollectedShare[]\n\n  /** Reconstructed CEK (only set when threshold met) */\n  public reconstructedCek?: string\n\n  /** DID of the session initiator */\n  public initiatedBy!: string\n\n  /** When the session expires */\n  public expiresAt?: Date\n\n  /** Purpose of this reconstruction session */\n  public purpose?: string\n\n  public static readonly type = 'ThresholdSessionRecord'\n  public readonly type = ThresholdSessionRecord.type\n\n  public constructor(props: ThresholdSessionRecordProps) {\n    super()\n\n    if (props) {\n      this.id = props.id ?? utils.uuid()\n      this.createdAt = props.createdAt ?? new Date()\n      this.updatedAt = props.updatedAt\n\n      this.sessionId = props.sessionId\n      this.vaultId = props.vaultId\n      this.docId = props.docId\n      this.threshold = props.threshold\n      this.totalShares = props.totalShares\n      this.status = props.status\n      this.shares = props.shares ?? []\n      this.reconstructedCek = props.reconstructedCek\n      this.initiatedBy = props.initiatedBy\n      this.expiresAt = props.expiresAt\n      this.purpose = props.purpose\n\n      this._tags = props.tags ?? {}\n    }\n  }\n\n  public getTags(): DefaultThresholdSessionTags & CustomThresholdSessionTags {\n    return {\n      ...this._tags,\n      sessionId: this.sessionId,\n      vaultId: this.vaultId,\n      docId: this.docId,\n      status: this.status,\n      initiatedBy: this.initiatedBy,\n    }\n  }\n\n  /**\n   * Check if threshold has been met\n   */\n  public get isThresholdMet(): boolean {\n    return this.shares.length >= this.threshold\n  }\n\n  /**\n   * Check if session has expired\n   */\n  public get isExpired(): boolean {\n    if (!this.expiresAt) return false\n    return new Date() > this.expiresAt\n  }\n\n  /**\n   * Get number of shares still needed\n   */\n  public get sharesNeeded(): number {\n    return Math.max(0, this.threshold - this.shares.length)\n  }\n\n  /**\n   * Check if a participant has already provided a share\n   */\n  public hasShareFrom(participantDid: string): boolean {\n    return this.shares.some((share) => share.providedBy === participantDid)\n  }\n\n  /**\n   * Add a share to the collection\n   */\n  public addShare(share: CollectedShare): void {\n    // Don't add duplicate shares from same participant\n    if (this.hasShareFrom(share.providedBy)) {\n      return\n    }\n    this.shares.push(share)\n    this.updatedAt = new Date()\n  }\n}\n"],"mappings":";;;;;;;;;AA6DA,IAAa,yBAAb,MAAa,+BAA+B,WAAoE;CAqC9G,AAAO,YAAY,OAAoC;AACrD,SAAO;OAHO,OAAO,uBAAuB;AAK5C,MAAI,OAAO;AACT,QAAK,KAAK,MAAM,MAAM,MAAM,MAAM;AAClC,QAAK,YAAY,MAAM,6BAAa,IAAI,MAAM;AAC9C,QAAK,YAAY,MAAM;AAEvB,QAAK,YAAY,MAAM;AACvB,QAAK,UAAU,MAAM;AACrB,QAAK,QAAQ,MAAM;AACnB,QAAK,YAAY,MAAM;AACvB,QAAK,cAAc,MAAM;AACzB,QAAK,SAAS,MAAM;AACpB,QAAK,SAAS,MAAM,UAAU,EAAE;AAChC,QAAK,mBAAmB,MAAM;AAC9B,QAAK,cAAc,MAAM;AACzB,QAAK,YAAY,MAAM;AACvB,QAAK,UAAU,MAAM;AAErB,QAAK,QAAQ,MAAM,QAAQ,EAAE;;;CAIjC,AAAO,UAAoE;AACzE,SAAO;GACL,GAAG,KAAK;GACR,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,aAAa,KAAK;GACnB;;;;;CAMH,IAAW,iBAA0B;AACnC,SAAO,KAAK,OAAO,UAAU,KAAK;;;;;CAMpC,IAAW,YAAqB;AAC9B,MAAI,CAAC,KAAK,UAAW,QAAO;AAC5B,yBAAO,IAAI,MAAM,GAAG,KAAK;;;;;CAM3B,IAAW,eAAuB;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,YAAY,KAAK,OAAO,OAAO;;;;;CAMzD,AAAO,aAAa,gBAAiC;AACnD,SAAO,KAAK,OAAO,MAAM,UAAU,MAAM,eAAe,eAAe;;;;;CAMzE,AAAO,SAAS,OAA6B;AAE3C,MAAI,KAAK,aAAa,MAAM,WAAW,CACrC;AAEF,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,4BAAY,IAAI,MAAM;;;uBA5EN,OAAO"}