import { isDryRunResult, trelloRequest } from './client.ts'
import { parseAuthInput } from './auth.ts'
import { LIST_FIELDS, listSummary, mapMany, requireMapped } from './models.ts'
import type {
DryRunResult,
MutationInput,
TrelloAuthInput,
TrelloListSummary,
} from './types.ts'
import {
clampInt,
compactRecord,
optionalBoolean,
optionalString,
requireRecord,
requireString,
} from './types.ts'
export type ListListsInput = TrelloAuthInput & {
boardId: string
filter?: 'open' | 'closed' | 'all'
limit?: number
}
export type GetListInput = TrelloAuthInput & {
listId: string
}
export type CreateListInput = TrelloAuthInput &
MutationInput & {
boardId: string
name: string
pos?: string | number
}
export type UpdateListInput = TrelloAuthInput &
MutationInput & {
listId: string
name?: string
closed?: boolean
pos?: string | number
boardId?: string
}
function listFilter(value: unknown): 'open' | 'closed' | 'all' {
if (value === undefined || value === null || value === '') return 'open'
const filter = requireString(value, 'filter')
if (filter !== 'open' && filter !== 'closed' && filter !== 'all') {
throw new Error('filter must be "open", "closed", or "all".')
}
return filter
}
function optionalPos(value: unknown, label: string): string | number | undefined {
if (value === undefined || value === null) return undefined
if (typeof value === 'number' && Number.isFinite(value)) return value
return requireString(value, label)
}
/**
* List lists on a caller-supplied board id.
* @example
* import { listLists } from 'kody:@kody/trello/lists'
* const lists = await listLists({ boardId })
*/
export async function listLists(input: ListListsInput): Promise<Array<TrelloListSummary>> {
const boardId = requireString(input.boardId, 'boardId')
const limit = clampInt(input.limit, 1, 200, 50)
const result = await trelloRequest<unknown>({
...input,
method: 'GET',
path: `/boards/${boardId}/lists`,
query: compactRecord({
filter: listFilter(input.filter),
fields: LIST_FIELDS,
}),
})
if (isDryRunResult(result)) return []
return mapMany(result.data, listSummary).slice(0, limit)
}
/**
* Get one Trello list by id supplied by the caller.
* @example
* import { getList } from 'kody:@kody/trello/lists'
* const list = await getList({ listId })
*/
export async function getList(input: GetListInput): Promise<TrelloListSummary> {
const listId = requireString(input.listId, 'listId')
const result = await trelloRequest<Record<string, unknown>>({
...input,
method: 'GET',
path: `/lists/${listId}`,
query: { fields: LIST_FIELDS },
})
if (isDryRunResult(result)) {
throw new Error('getList is read-only and does not support dryRun.')
}
return requireMapped(listSummary(result.data), 'getList')
}
/**
* Create a Trello list. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import { createList } from 'kody:@kody/trello/lists'
* const preview = await createList({ boardId, name: 'In review', dryRun: true })
*/
export async function createList(input: CreateListInput): Promise<TrelloListSummary | DryRunResult> {
const body = compactRecord({
idBoard: requireString(input.boardId, 'boardId'),
name: requireString(input.name, 'name'),
pos: optionalPos(input.pos, 'pos'),
})
const result = await trelloRequest<Record<string, unknown>>({
...input,
method: 'POST',
path: '/lists',
query: body,
body,
})
if (isDryRunResult(result)) return result
return requireMapped(listSummary(result.data), 'createList')
}
/**
* Update a Trello list. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import { updateList } from 'kody:@kody/trello/lists'
* const preview = await updateList({ listId, name: 'Done', dryRun: true })
*/
export async function updateList(input: UpdateListInput): Promise<TrelloListSummary | DryRunResult> {
const listId = requireString(input.listId, 'listId')
const body = compactRecord({
name: optionalString(input.name, 'name'),
closed: optionalBoolean(input.closed, 'closed'),
pos: optionalPos(input.pos, 'pos'),
idBoard: optionalString(input.boardId, 'boardId'),
})
if (Object.keys(body).length === 0) {
throw new Error('updateList requires at least one of name, closed, pos, or boardId.')
}
const result = await trelloRequest<Record<string, unknown>>({
...input,
method: 'PUT',
path: `/lists/${listId}`,
query: body,
body,
})
if (isDryRunResult(result)) return result
return requireMapped(listSummary(result.data), 'updateList')
}
export { listSummary }
/**
* List helpers dispatcher.
* @example
* import lists from 'kody:@kody/trello/lists'
* const items = await lists({ action: 'list', boardId })
*/
export default async function listsEntrypoint(params: Record<string, unknown> = {}) {
const input = requireRecord(params, 'lists')
const auth = parseAuthInput(input)
const action = optionalString(input.action, 'action') ?? 'list'
switch (action) {
case 'list':
return listLists({
...auth,
boardId: requireString(input.boardId, 'boardId'),
filter: listFilter(input.filter),
limit: input.limit as number | undefined,
})
case 'get':
return getList({ ...auth, listId: requireString(input.listId, 'listId') })
case 'create':
return createList({
...auth,
boardId: requireString(input.boardId, 'boardId'),
name: requireString(input.name, 'name'),
pos: optionalPos(input.pos, 'pos'),
confirm: optionalBoolean(input.confirm, 'confirm'),
dryRun: optionalBoolean(input.dryRun, 'dryRun'),
})
case 'update':
return updateList({
...auth,
listId: requireString(input.listId, 'listId'),
name: optionalString(input.name, 'name'),
closed: optionalBoolean(input.closed, 'closed'),
pos: optionalPos(input.pos, 'pos'),
boardId: optionalString(input.boardId, 'boardId'),
confirm: optionalBoolean(input.confirm, 'confirm'),
dryRun: optionalBoolean(input.dryRun, 'dryRun'),
})
default:
throw new Error('lists action must be one of: list, get, create, update.')
}
}