Skip to content
← Public packages

@kody/salesforce

Salesforce REST helpers for identity, SOQL, multi-org routing, and error investigation.

src/types.ts

126 lines · 3.5 KB · TypeScript
export type JsonRecord = Record<string, unknown>

export type SalesforceHttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'

export type SalesforceRequestInput = {
	/** Absolute URL, path beginning with `/`, or REST path under the versioned data root. */
	path: string
	method?: SalesforceHttpMethod
	query?: Record<string, string | number | boolean | undefined | null>
	body?: unknown
	headers?: Record<string, string>
	/** Override auto-discovered org instance URL, e.g. `https://example.my.salesforce.com`. */
	instanceUrl?: string
	/** REST API version such as `v61.0`. Defaults to `v61.0`. */
	apiVersion?: string
	/** Saved OAuth integration name. Defaults to `salesforce`; set for alternate orgs, e.g. `salesforce-sandbox`. */
	integrationName?: string
	confirm?: boolean
	dryRun?: boolean
}

export type SalesforceRequestResult = {
	ok: boolean
	status: number
	statusText: string
	headers: Record<string, string>
	body: unknown
}

export type SalesforceUserInfo = {
	sub?: string
	user_id: string
	organization_id: string
	preferred_username?: string
	nickname?: string
	name?: string
	email?: string
	email_verified?: boolean
	given_name?: string
	family_name?: string
	zoneinfo?: string
	photos?: { picture?: string; thumbnail?: string }
	profile?: string
	picture?: string
	address?: JsonRecord
	urls?: {
		enterprise?: string
		metadata?: string
		partner?: string
		rest?: string
		sobjects?: string
		search?: string
		query?: string
		recent?: string
		profile?: string
		feeds?: string
		feed_items?: string
		groups?: string
		users?: string
		custom_domain?: string
		[key: string]: string | undefined
	}
	active?: boolean
	user_type?: string
	language?: string
	locale?: string
	utcOffset?: number
	updated_at?: string
	is_app_installed?: boolean
	[key: string]: unknown
}

export function requireRecord(value: unknown, label: string): JsonRecord {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		throw new Error(`${label} must be an object.`)
	}
	return value as JsonRecord
}

export function requireString(
	value: unknown,
	label: string,
	options: { allowEmpty?: boolean } = {},
): string {
	if (typeof value !== 'string' || (!options.allowEmpty && value.trim().length === 0)) {
		throw new Error(
			`${label} must be ${options.allowEmpty ? 'a string' : 'a non-empty string'}.`,
		)
	}
	return options.allowEmpty ? value : value.trim()
}

export function optionalBoolean(value: unknown, label: string): boolean | undefined {
	if (value === undefined) return undefined
	if (typeof value !== 'boolean') throw new Error(`${label} must be a boolean.`)
	return value
}

export function optionalString(value: unknown, label: string): string | undefined {
	if (value === undefined) return undefined
	return requireString(value, label, { allowEmpty: true })
}

export function requireSObjectName(value: unknown, label = 'sobject'): string {
	const name = requireString(value, label)
	if (!/^[A-Za-z][A-Za-z0-9_]{0,79}$/.test(name)) {
		throw new Error(`${label} must be a Salesforce sObject API name.`)
	}
	return name
}

export function requireSalesforceId(value: unknown, label = 'id'): string {
	const id = requireString(value, label)
	if (!/^[a-zA-Z0-9]{15}([a-zA-Z0-9]{3})?$/.test(id)) {
		throw new Error(`${label} must be a 15- or 18-character Salesforce id.`)
	}
	return id
}

export function requireApiVersion(value: unknown, label = 'apiVersion'): string {
	const version = requireString(value, label)
	if (!/^v\d+\.\d+$/.test(version)) {
		throw new Error(`${label} must look like "v61.0".`)
	}
	return version
}