import type {
IntercomArticle,
IntercomContact,
IntercomConversation,
IntercomMe,
IntercomPageInfo,
JsonRecord,
} from './types.ts'
function asRecord(value: unknown): JsonRecord {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as JsonRecord)
: {}
}
function asString(value: unknown): string | null {
return typeof value === 'string' && value.length > 0 ? value : null
}
function asNumber(value: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? value : null
}
function asBoolean(value: unknown): boolean | null {
return typeof value === 'boolean' ? value : null
}
export function mapMe(value: unknown): IntercomMe {
const record = asRecord(value)
const app = asRecord(record.app)
const id = asString(record.id)
if (!id) throw new Error('Intercom /me did not return an admin id.')
return {
id,
type: asString(record.type) ?? 'admin',
hasInboxSeat: asBoolean(record.has_inbox_seat),
app: {
idCode: asString(app.id_code),
region: asString(app.region),
},
}
}
export function mapContact(value: unknown): IntercomContact | null {
const record = asRecord(value)
const id = asString(record.id)
if (!id) return null
return {
id,
role: asString(record.role),
email: asString(record.email),
name: asString(record.name),
externalId: asString(record.external_id),
createdAt: asNumber(record.created_at),
updatedAt: asNumber(record.updated_at),
}
}
export function mapConversation(value: unknown): IntercomConversation | null {
const record = asRecord(value)
const id = asString(record.id)
if (!id) return null
const contacts = asRecord(record.contacts)
const contactItems = Array.isArray(contacts.contacts) ? contacts.contacts : []
const source = asRecord(record.source)
return {
id,
title: asString(record.title) ?? asString(source.subject),
state: asString(record.state),
open: asBoolean(record.open),
createdAt: asNumber(record.created_at),
updatedAt: asNumber(record.updated_at),
contactIds: contactItems
.map((item) => asString(asRecord(item).id))
.filter((item): item is string => Boolean(item)),
}
}
export function mapArticle(value: unknown): IntercomArticle | null {
const record = asRecord(value)
const id = asString(record.id) ?? (record.id != null ? String(record.id) : null)
if (!id) return null
return {
id,
title: asString(record.title),
state: asString(record.state),
url: asString(record.url),
authorId:
asString(record.author_id) ??
(record.author_id != null ? String(record.author_id) : null),
createdAt: asNumber(record.created_at),
updatedAt: asNumber(record.updated_at),
}
}
export function extractPageInfo(payload: unknown): IntercomPageInfo {
const record = asRecord(payload)
const pages =
record.pages && typeof record.pages === 'object' ? asRecord(record.pages) : null
const next = pages?.next
let startingAfter: string | null = null
if (next && typeof next === 'object') {
const cursor = (next as { starting_after?: unknown }).starting_after
if (typeof cursor === 'string' && cursor.length > 0) startingAfter = cursor
} else if (typeof next === 'string' && next.length > 0) {
startingAfter = next
}
const page = typeof pages?.page === 'number' ? pages.page : null
const perPage = typeof pages?.per_page === 'number' ? pages.per_page : null
const totalPages = typeof pages?.total_pages === 'number' ? pages.total_pages : null
const totalCount = typeof record.total_count === 'number' ? record.total_count : null
return {
hasNextPage: Boolean(startingAfter) || (totalPages !== null && page !== null && page < totalPages),
startingAfter,
page,
perPage,
totalPages,
totalCount,
}
}
export function extractListItems(payload: unknown): Array<unknown> {
if (Array.isArray(payload)) return payload
const record = asRecord(payload)
if (Array.isArray(record.data)) return record.data
if (Array.isArray(record.articles)) return record.articles
if (Array.isArray(record.conversations)) return record.conversations
if (Array.isArray(record.contacts)) return record.contacts
return []
}
export function compactDefined<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
}