{"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/modules/vc/validators.ts"],"sourcesContent":["import type { ValidationOptions } from 'class-validator'\n\nimport { buildMessage, isString, isURL, ValidateBy } from 'class-validator'\nimport { CredoError } from '../../error'\nimport { isJsonObject } from '../../types'\nimport {\n  CREDENTIALS_CONTEXT_V1_URL,\n  CREDENTIALS_CONTEXT_V2_URL,\n  VERIFIABLE_CREDENTIAL_TYPE,\n  VERIFIABLE_PRESENTATION_TYPE,\n} from './constants'\n\nexport interface IsCredentialJsonLdContextValidationOptions extends ValidationOptions {\n  /**\n   * Whether to allow string value in addition to arrays.\n   *\n   * @default false\n   */\n  allowString?: boolean\n\n  /**\n   * The expected credential context URL.\n   *\n   * @default {@link CREDENTIALS_CONTEXT_V1_URL}\n   */\n  credentialContext?: string\n}\n\nexport function IsCredentialJsonLdContext(\n  validationOptions?: IsCredentialJsonLdContextValidationOptions\n): PropertyDecorator {\n  const allowString = validationOptions?.allowString ?? false\n  const credentialContext = validationOptions?.credentialContext ?? CREDENTIALS_CONTEXT_V1_URL\n\n  return ValidateBy(\n    {\n      name: 'IsCredentialJsonLdContext',\n      validator: {\n        validate: (value): boolean => {\n          if (!Array.isArray(value)) return allowString && isString(value) && value === credentialContext\n\n          // First item must be the verifiable credential context\n          if (value[0] !== credentialContext) return false\n\n          return value.every((v) => (isString(v) && isURL(v)) || isJsonObject(v))\n        },\n        defaultMessage: buildMessage(\n          (eachPrefix) =>\n            `${eachPrefix}$property must be an array of strings or objects, where the first item is the verifiable credential context URL.`,\n          validationOptions\n        ),\n      },\n    },\n    validationOptions\n  )\n}\n\nexport function IsCredentialType(validationOptions?: ValidationOptions): PropertyDecorator {\n  return ValidateBy(\n    {\n      name: 'IsVerifiableCredentialType',\n      validator: {\n        validate: (value): boolean => {\n          return Array.isArray(value)\n            ? value.includes(VERIFIABLE_CREDENTIAL_TYPE) && value.every((v) => typeof v === 'string')\n            : value === VERIFIABLE_CREDENTIAL_TYPE\n        },\n        defaultMessage: buildMessage(\n          (eachPrefix) =>\n            `${eachPrefix}$property must be \"VerifiableCredential\" or an array of strings which includes \"VerifiableCredential\"`,\n          validationOptions\n        ),\n      },\n    },\n    validationOptions\n  )\n}\n\nexport function IsVerifiablePresentationType(validationOptions?: ValidationOptions): PropertyDecorator {\n  return ValidateBy(\n    {\n      name: 'IsVerifiablePresentationType',\n      validator: {\n        validate: (value): boolean => {\n          return Array.isArray(value)\n            ? value.includes(VERIFIABLE_PRESENTATION_TYPE) && value.every((v) => typeof v === 'string')\n            : value === VERIFIABLE_PRESENTATION_TYPE\n        },\n        defaultMessage: buildMessage(\n          (eachPrefix) =>\n            `${eachPrefix}$property must be \"VerifiablePresentation\" or an array of strings which includes \"VerifiablePresentation\"`,\n          validationOptions\n        ),\n      },\n    },\n    validationOptions\n  )\n}\n\nexport interface ValidationResult {\n  isValid: boolean\n  error?: Error\n}\n\nexport interface ValidateVc2CredentialValidityPeriodOptions {\n  validFrom?: string\n  validUntil?: string\n  skewSeconds: number\n  now?: number\n}\n\nexport function validateVc2ContextBaseline(context: unknown): ValidationResult {\n  const contextArray = Array.isArray(context) ? context : typeof context === 'string' ? [context] : undefined\n\n  if (!contextArray) {\n    return {\n      isValid: false,\n      error: new CredoError('VC2 @context must be an ordered set'),\n    }\n  }\n\n  if (contextArray.length === 0) {\n    return {\n      isValid: false,\n      error: new CredoError('VC2 @context must not be empty'),\n    }\n  }\n\n  if (contextArray[0] !== CREDENTIALS_CONTEXT_V2_URL) {\n    return {\n      isValid: false,\n      error: new CredoError(`VC2 @context must start with '${CREDENTIALS_CONTEXT_V2_URL}'`),\n    }\n  }\n\n  const hasOnlyValidEntries = contextArray.every((entry) => {\n    if (typeof entry === 'string') return isURL(entry)\n    return isJsonObject(entry)\n  })\n\n  if (!hasOnlyValidEntries) {\n    return {\n      isValid: false,\n      error: new CredoError('VC2 @context entries must be URLs or JSON objects'),\n    }\n  }\n\n  return {\n    isValid: true,\n  }\n}\n\nexport function validateVc2CredentialStatus(options: {\n  credentialStatus?: unknown\n  credentialFormat: 'JWT' | 'SD-JWT' | 'DI'\n  verifyCredentialStatus?: boolean\n}): ValidationResult {\n  if (!options.verifyCredentialStatus || !options.credentialStatus) {\n    return {\n      isValid: true,\n    }\n  }\n\n  return {\n    isValid: false,\n    error: new CredoError(\n      `Verifying credential status is not supported for W3C VC2 ${options.credentialFormat} credentials`\n    ),\n  }\n}\n\nexport function validateVc2CredentialValidityPeriod(\n  options: ValidateVc2CredentialValidityPeriodOptions\n): ValidationResult {\n  const now = options.now ?? Math.floor(Date.now() / 1000)\n\n  const validFromTimestamp = options.validFrom ? Math.floor(Date.parse(options.validFrom) / 1000) : undefined\n  const validUntilTimestamp = options.validUntil ? Math.floor(Date.parse(options.validUntil) / 1000) : undefined\n\n  if (\n    validFromTimestamp !== undefined &&\n    validUntilTimestamp !== undefined &&\n    validFromTimestamp > validUntilTimestamp\n  ) {\n    return {\n      isValid: false,\n      error: new CredoError('Credential validFrom must be temporally equal to or before validUntil'),\n    }\n  }\n\n  if (validFromTimestamp !== undefined && validFromTimestamp - options.skewSeconds > now) {\n    return {\n      isValid: false,\n      error: new CredoError('Credential is not valid yet based on validFrom'),\n    }\n  }\n\n  if (validUntilTimestamp !== undefined && validUntilTimestamp + options.skewSeconds < now) {\n    return {\n      isValid: false,\n      error: new CredoError('Credential is no longer valid based on validUntil'),\n    }\n  }\n\n  return {\n    isValid: true,\n  }\n}\n"],"mappings":"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"}