Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/meta

src/validation.ts

74 lines · 2.5 KB · TypeScript
export type InputRecord = Record<string, unknown>

export function inputRecord(value: unknown): InputRecord {
	if (value === null || typeof value !== 'object' || Array.isArray(value)) {
		throw new Error('Input must be an object.')
	}
	return value as InputRecord
}

export function requiredString(input: InputRecord, key: string): string {
	const value = input[key]
	if (typeof value !== 'string' || value.trim().length === 0) {
		throw new Error(key + ' is required.')
	}
	return value.trim()
}

export function optionalString(input: InputRecord, key: string): string | undefined {
	const value = input[key]
	if (value === undefined || value === null || value === '') return undefined
	if (typeof value !== 'string') throw new Error(key + ' must be a string.')
	return value
}

export function optionalBoolean(input: InputRecord, key: string): boolean | undefined {
	const value = input[key]
	if (value === undefined || value === null) return undefined
	if (typeof value !== 'boolean') throw new Error(key + ' must be a boolean.')
	return value
}

export function optionalNumber(input: InputRecord, key: string): number | undefined {
	const value = input[key]
	if (value === undefined || value === null || value === '') return undefined
	if (typeof value !== 'number' || !Number.isFinite(value)) {
		throw new Error(key + ' must be a number.')
	}
	return value
}

export function boundedLimit(value: unknown, fallback = 10, max = 50): number {
	if (value === undefined || value === null) return fallback
	if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > max) {
		throw new Error('limit must be an integer from 1 through ' + max + '.')
	}
	return Number(value)
}

export function optionalRecord(input: InputRecord, key: string): InputRecord | undefined {
	const value = input[key]
	if (value === undefined || value === null) return undefined
	if (typeof value !== 'object' || Array.isArray(value)) {
		throw new Error(key + ' must be an object.')
	}
	return value as InputRecord
}

const TOKEN_KEYS = /^(access_token|accessToken|page_access_token|pageAccessToken)$/

/** Strip token fields from Graph payloads so helpers never leak credentials. */
export function stripSecrets<T>(value: T): T {
	if (Array.isArray(value)) {
		return value.map((item) => stripSecrets(item)) as T
	}
	if (value && typeof value === 'object') {
		const out: Record<string, unknown> = {}
		for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
			if (TOKEN_KEYS.test(key)) continue
			out[key] = stripSecrets(nested)
		}
		return out as T
	}
	return value
}