All files / src/helpers cas.ts

100% Statements 56/56
100% Branches 17/17
100% Functions 4/4
100% Lines 56/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 1151x 1x 1x 1x 1x 1x 1x 1x   1x   1x         1x   1x                             1x 1x 149x 149x 149x 149x   1x                         1x 1x 7x 7x 7x   6x 7x 4x 7x 1x 1x 1x 7x   1x           1x 1x 2x 2x   2x 2x 1x 1x   1x                             1x 1x 6x 6x 6x   5x 6x 4x 4x 6x 1x 1x 1x 6x  
import { CAS_REGEX } from '@/constants/common';
// `isCAS` lives in typeGuards/common; imported for internal use only.
// Deliberately not re-exported: doing so made every isCAS consumer depend on
// this module, creating a common.ts -> smiles.ts -> cas.ts -> common.ts cycle
// that Rollup could not split cleanly across chunks.
import { isCAS } from '@/utils/typeGuards/common';
import { Logger } from '@/utils/Logger';
import { getErrorMessage } from '@/helpers/exceptions';
 
const logger = new Logger('cas');
 
/**
 * @category CAS Helpers
 * @categoryDescription Chemical Abstracts Service (CAS) number validation and parsing utilities.
 * @showCategories
 * @source
 */
 
/**
 * Searches for a valid CAS number within a string.
 * Returns the first valid CAS number found, or undefined if none are found.
 * @category CAS Helpers
 * @param data - The string to search for a CAS number
 * @returns The first valid CAS number found, or undefined
 *
 * @example
 * ```typescript
 * findCAS('Example of a valid cas: 1234-56-6..') // Returns '1234-56-6'
 * findCAS('and 50-00-0 is another valid cas #') // Returns '50-00-0'
 * findCAS('Example of an invalid cas: 1232-56-6..') // Returns undefined
 * findCAS('and 50-00-1 is another valid cas #') // Returns undefined
 * ```
 * @source
 */
export function findCAS(data: string): CAS<string> | void {
  const regex = RegExp(CAS_REGEX.source, 'g');
  const match = data.match(regex);
  if (match && isCAS(match[0])) return match[0];
}
 
/**
 * Gets the names of a CAS number from the Cactus API.
 * @category CAS Helpers
 * @param cas - The CAS number to get the names of
 * @returns The names of the CAS number
 * @example
 * ```typescript
 * getNamesByCAS("79-11-8")
 * // Returns ['2-chloroacetic acid', '2-chloroethanoic acid', 'Acide chloracetique', ...etc]
 * getNamesByCAS("1234567890")
 * // Returns undefined
 * ```
 * @source
 */
export async function getNamesByCAS(cas: CAS<string>): Promise<Maybe<string[]>> {
  try {
    const response = await fetch(
      `https://cactus.nci.nih.gov/chemical/structure/${encodeURIComponent(cas)}/names`,
    );
    const data = await response.text();
    if (!data) return;
    return data.split('\n').map((line: string) => line.trim());
  } catch (error) {
    logger.error(`Failed to look up names by CAS: ${getErrorMessage(error)}`, { error });
    return;
  }
}
 
/**
 * Gets the IUPAC name of a chemical from the Cactus API.
 * @category CAS Helpers
 * @param name - The name of the chemical to get the IUPAC name of
 * @returns The IUPAC name of the chemical
 * @source
 */
export async function getIUPACName(name: string): Promise<Maybe<string>> {
  const response = await fetch(
    `https://cactus.nci.nih.gov/chemical/structure/${encodeURIComponent(name)}/iupac_name`,
  );
  const data = await response.text();
  if (!data) return;
  return data.trim();
}
 
/**
 * Gets the CAS of a chemical using the name from the Cactus API.
 * @category CAS Helpers
 * @param name - The chemical name to lookup
 * @returns The CAS number of the chemical
 * @example
 * ```typescript
 * getCASByName("Acetic Acid")
 * // Returns '79-11-8'
 * getCASByName("Acide chloracetique")
 * // Returns '79-11-8'
 * getCASByName("adsfasfd")
 * // Returns undefined
 * ```
 * @source
 */
export async function getCASByName(name: string): Promise<Maybe<CAS<string>>> {
  try {
    const response = await fetch(
      `https://cactus.nci.nih.gov/chemical/structure/${encodeURIComponent(name)}/cas`,
    );
    const data = await response.text();
    if (typeof data === 'undefined') return;
    const casList = data.split('\n').find((cas) => isCAS(cas));
    return casList;
  } catch (error) {
    logger.error(`Failed to look up CAS by name: ${getErrorMessage(error)}`, { error });
    return;
  }
}