import Cookie from 'cookie'; export function isApiError(data: Record): data is APIError { return 'error' in data; } /** * Type guard to determine if a value is not null, undefined, or empty. */ export function isTruthy( value: V, ): value is NonNullable { const badStrings = ['', 'null', 'undefined']; if (typeof value === 'string' && !badStrings.includes(value)) { return true; } else if (typeof value === 'number') { return true; } else if (typeof value === 'boolean') { return true; } return false; } /** * Retrieve the CSRF token from cookie storage. */ export function getCsrfToken(): string { const { csrftoken: csrfToken } = Cookie.parse(document.cookie); if (typeof csrfToken === 'undefined') { throw new Error('Invalid or missing CSRF token'); } return csrfToken; } /** * Fetch data from the NetBox API (authenticated). * @param url API endpoint */ export async function getApiData( url: string, ): Promise | APIError> { const token = getCsrfToken(); const res = await fetch(url, { method: 'GET', headers: { 'X-CSRFToken': token }, }); const json = (await res.json()) as APIAnswer | APIError; return json; } export function getElements( key: K, ): Generator; export function getElements( key: K, ): Generator; export function getElements(key: string): Generator; export function* getElements( key: string | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap, ) { for (const element of document.querySelectorAll(key)) { if (element !== null) { yield element; } } } /** * scrollTo() wrapper that calculates a Y offset relative to `element`, but also factors in an * offset relative to div#content-title. This ensures we scroll to the element, but leave enough * room to see said element. * * @param element Element to scroll to * @param offset Y Offset. 0 by default, to take into account the NetBox header. */ export function scrollTo(element: Element, offset: number = 0): void { let yOffset = offset; const title = document.getElementById('content-title') as Nullable; if (title !== null) { // If the #content-title element exists, add it to the offset. yOffset += title.getBoundingClientRect().bottom; } // Calculate the scrollTo target. const top = element.getBoundingClientRect().top + window.pageYOffset + yOffset; // Scroll to the calculated location. window.scrollTo({ top, behavior: 'smooth' }); return; }