import { parseAuth } from './auth.ts' import { intercomRequest } from './client.ts' import { compactDefined, extractListItems, mapArticle } from './models.ts' import { getMe } from './me.ts' import type { DryRunResult, IntercomArticle, IntercomAuthInput, IntercomPageInfo, JsonRecord, MutationInput, } from './types.ts' import { clampInt, optionalBoolean, optionalString, requireRecord, requireString, } from './types.ts' export type ListArticlesInput = IntercomAuthInput & { perPage?: number page?: number } export type GetArticleInput = IntercomAuthInput & { id: string } export type CreateArticleInput = IntercomAuthInput & MutationInput & { title: string body?: string authorId?: string state?: string parentId?: number | string parentType?: string } export type UpdateArticleInput = IntercomAuthInput & MutationInput & { id: string title?: string body?: string authorId?: string state?: string parentId?: number | string parentType?: string } export type ArticleListResult = { items: Array pageInfo: IntercomPageInfo } async function resolveAuthorId(input: IntercomAuthInput & { authorId?: string }): Promise { if (input.authorId) return requireString(input.authorId, 'authorId') const me = await getMe(input) return me.id } function articleBody( input: CreateArticleInput | UpdateArticleInput, authorId: string, ): JsonRecord { return compactDefined({ title: 'title' in input ? input.title : undefined, body: input.body, author_id: authorId, state: input.state, parent_id: input.parentId, parent_type: input.parentType, }) } /** * List Intercom Help Center articles. * @example * import { listArticles } from 'kody:@kody/intercom/articles' * const { items } = await listArticles({ perPage: 10 }) */ export async function listArticles(input: ListArticlesInput = {}): Promise { const result = await intercomRequest({ ...input, operation: 'articles.read', path: '/articles', query: compactDefined({ per_page: clampInt(input.perPage, 1, 150, 20), page: input.page === undefined ? undefined : clampInt(input.page, 1, 10_000, 1, 'page'), }), }) if ('dryRun' in result) { throw new Error('listArticles is read-only.') } return { items: extractListItems(result.data) .map(mapArticle) .filter((item): item is IntercomArticle => Boolean(item)), pageInfo: result.pageInfo, } } /** * Get one Intercom article by id. * @example * import { getArticle } from 'kody:@kody/intercom/articles' * const article = await getArticle({ id }) */ export async function getArticle(input: GetArticleInput): Promise { const id = requireString(input.id, 'id') const result = await intercomRequest({ ...input, operation: 'articles.read', path: `/articles/${encodeURIComponent(id)}`, }) if ('dryRun' in result) { throw new Error('getArticle is read-only.') } const mapped = mapArticle(result.data) if (!mapped) throw new Error('Intercom did not return an article id.') return mapped } /** * Create an Intercom article. Requires `confirm: true`, or use `dryRun: true`. * When `authorId` is omitted, the connected `/me` admin is used. * @example * import { createArticle } from 'kody:@kody/intercom/articles' * const preview = await createArticle({ title: 'Reset your password', dryRun: true }) */ export async function createArticle( input: CreateArticleInput, ): Promise { const title = requireString(input.title, 'title') const authorId = input.dryRun ? input.authorId ?? '' : await resolveAuthorId(input) const result = await intercomRequest({ ...input, operation: 'articles.write', method: 'POST', path: '/articles', body: articleBody({ ...input, title }, authorId), }) if ('dryRun' in result) return result const mapped = mapArticle(result.data) if (!mapped) throw new Error('Intercom did not return a created article id.') return mapped } /** * Update an Intercom article. Requires `confirm: true`, or use `dryRun: true`. * @example * import { updateArticle } from 'kody:@kody/intercom/articles' * const preview = await updateArticle({ id, title: 'Updated title', dryRun: true }) */ export async function updateArticle( input: UpdateArticleInput, ): Promise { const id = requireString(input.id, 'id') const authorId = input.dryRun ? input.authorId ?? '' : input.authorId ? requireString(input.authorId, 'authorId') : undefined const result = await intercomRequest({ ...input, operation: 'articles.write', method: 'PUT', path: `/articles/${encodeURIComponent(id)}`, body: compactDefined({ title: input.title, body: input.body, author_id: authorId, state: input.state, parent_id: input.parentId, parent_type: input.parentType, }), }) if ('dryRun' in result) return result const mapped = mapArticle(result.data) if (!mapped) throw new Error('Intercom did not return an updated article id.') return mapped } export function parseListArticles(params: Record): ListArticlesInput { const input = requireRecord(params, 'list-articles') return { ...parseAuth(input), perPage: input.perPage as number | undefined, page: input.page as number | undefined, } } export function parseGetArticle(params: Record): GetArticleInput { const input = requireRecord(params, 'get-article') return { ...parseAuth(input), id: requireString(input.id, 'id') } } export function parseCreateArticle(params: Record): CreateArticleInput { const input = requireRecord(params, 'create-article') return { ...parseAuth(input), title: requireString(input.title, 'title'), body: optionalString(input.body, 'body'), authorId: optionalString(input.authorId, 'authorId'), state: optionalString(input.state, 'state'), parentId: input.parentId as number | string | undefined, parentType: optionalString(input.parentType, 'parentType'), confirm: optionalBoolean(input.confirm, 'confirm'), dryRun: optionalBoolean(input.dryRun, 'dryRun'), } } export function parseUpdateArticle(params: Record): UpdateArticleInput { const input = requireRecord(params, 'update-article') return { ...parseAuth(input), id: requireString(input.id, 'id'), title: optionalString(input.title, 'title'), body: optionalString(input.body, 'body'), authorId: optionalString(input.authorId, 'authorId'), state: optionalString(input.state, 'state'), parentId: input.parentId as number | string | undefined, parentType: optionalString(input.parentType, 'parentType'), confirm: optionalBoolean(input.confirm, 'confirm'), dryRun: optionalBoolean(input.dryRun, 'dryRun'), } } /** * Intercom article helpers. Writes require `confirm: true` or `dryRun: true`. * @example * import { listArticles } from 'kody:@kody/intercom/articles' * const { items } = await listArticles({ perPage: 10 }) */ export default async function articlesEntrypoint(params: Record = {}) { return listArticles(parseListArticles(params)) }