export type JsonRecord = Record<string, unknown>
export type IntercomAuthMode = 'oauth' | 'accessToken'
export type IntercomRegion = 'us' | 'eu' | 'au'
export type IntercomAuthInput = {
/** Saved OAuth integration name. Defaults to `intercom`. */
integrationName?: string
/** Alias of `integrationName`. */
integration?: string
/**
* Short account alias. `work` → `intercom-work`. Values that already start
* with `intercom-` are used as-is. `default` / `intercom` / omitted → `intercom`.
*/
account?: string
/** User secret name for a workspace access token. Defaults to `intercomAccessToken`. */
secretName?: string
/** Force OAuth or access-token auth when both exist. */
auth?: IntercomAuthMode
/** Regional Intercom host. Defaults to `us`. Ignored when `apiBaseUrl` is set. */
region?: IntercomRegion
/** Absolute Intercom API origin, for example `https://api.eu.intercom.io`. */
apiBaseUrl?: string
/** Override the `Intercom-Version` header. Defaults to `2.14`. */
apiVersion?: string
}
export type MutationInput = {
/** Preview the REST call without contacting Intercom. */
dryRun?: boolean
/** Required for live writes after explicit user approval. */
confirm?: boolean
}
export type DryRunResult<T extends JsonRecord = JsonRecord> = {
dryRun: true
method: string
path: string
url: string
body?: T
}
export type IntercomPageInfo = {
hasNextPage: boolean
startingAfter: string | null
page: number | null
perPage: number | null
totalPages: number | null
totalCount: number | null
}
export type IntercomSearchQuery = {
field?: string
operator?: string
value?: unknown
}
export type IntercomMe = {
id: string
type: string
hasInboxSeat: boolean | null
app: {
idCode: string | null
region: string | null
}
}
export type IntercomContact = {
id: string
role: string | null
email: string | null
name: string | null
externalId: string | null
createdAt: number | null
updatedAt: number | null
}
export type IntercomConversation = {
id: string
title: string | null
state: string | null
open: boolean | null
createdAt: number | null
updatedAt: number | null
contactIds: Array<string>
}
export type IntercomArticle = {
id: string
title: string | null
state: string | null
url: string | null
authorId: string | null
createdAt: number | null
updatedAt: number | null
}
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 optionalString(value: unknown, label: string): string | undefined {
if (value === undefined || value === null) return undefined
return requireString(value, label)
}
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 clampInt(
value: unknown,
min: number,
max: number,
fallback: number,
label = 'perPage',
): number {
if (value === undefined || value === null) return fallback
if (typeof value !== 'number' || !Number.isInteger(value)) {
throw new Error(`${label} must be an integer.`)
}
if (value < min || value > max) {
throw new Error(`${label} must be between ${min} and ${max}.`)
}
return value
}
export function compactRecord<T extends JsonRecord>(value: T): T {
const output: JsonRecord = {}
for (const [key, item] of Object.entries(value)) {
if (item === undefined) continue
output[key] = item
}
return output as T
}
export function parseAction<T extends string>(
value: unknown,
allowed: Array<T>,
fallback: T,
label: string,
): T {
if (value === undefined || value === null || value === '') return fallback
const action = requireString(value, 'action')
if (!allowed.includes(action as T)) {
throw new Error(`${label} action must be one of: ${allowed.join(', ')}.`)
}
return action as T
}
/**
* Shared Intercom types for this package.
* @example
* import types from 'kody:@kody/intercom/types'
* const catalog = await types()
*/
export default async function typesOverview() {
return {
auth: ['IntercomAuthInput', 'IntercomAuthMode', 'IntercomRegion'],
models: ['IntercomContact', 'IntercomConversation', 'IntercomArticle', 'IntercomMe'],
}
}