import urlJoin from "url-join"; interface BuildWispClient { baseUrl?: string; blogId: string; } export interface Author { name: string | null; image: string | null; } export interface TagInPost { id: string; name: string; } export interface PostPagination { page: number; limit: number | "all"; totalPages: number; totalPosts: number; nextPage: number | null; prevPage: number | null; } export interface GetPostsResult { posts: { id: string; createdAt: Date; teamId: string; description: string | null; title: string; slug: string; image: string | null; authorId: string; updatedAt: Date; publishedAt: Date | null; author: Author; tags: TagInPost[]; }[]; pagination: PostPagination; } export interface GetRelatedPostsResult { posts: { id: string; createdAt: Date; teamId: string; description: string | null; title: string; slug: string; image: string | null; authorId: string; updatedAt: Date; publishedAt: Date | null; distance: number; }[]; } export interface GetPostResult> { post: { id: string; createdAt: Date; teamId: string; description: string | null; title: string; content: string; slug: string; image: string | null; metadata: MetadataType | null; authorId: string; updatedAt: Date; publishedAt: Date | null; tags: TagInPost[]; author: Author; } | null; } export interface TagPagination { page: number; limit: number | "all"; totalPages: number; totalTags: number; nextPage: number | null; prevPage: number | null; } export interface GetTagsResult { tags: { id: string; name: string; description: string | null; }[]; pagination: TagPagination; } export interface GetCtasResult { ctas: { id: string; title: string; description: string | null; slug: string; teamId: string; createdAt: Date; updatedAt: Date; distance: number; }[]; } export interface ContentPagination { page: number; limit: number | "all"; totalPages: number; totalContents: number; nextPage: number | null; prevPage: number | null; } export type FieldType = | "text" | "textarea" | "rich_text" | "boolean" | "number" | "date" | "image" | "select" | "multi_select"; export interface BaseField { label: string; name: string; type: Exclude; required?: boolean; description?: string; } export interface SelectField extends Omit { type: "select"; options: string[]; } export interface MultiSelectField extends Omit { type: "multi_select"; options: string[]; } export type Field = BaseField | SelectField | MultiSelectField; export interface VersionedField { version: string; fields: Field[]; } export interface ContentTypeMetadata { name: string; slug: string; schema: VersionedField; } export interface Content> { id: string; slug: string; publishedAt: Date | null; createdAt: Date; updatedAt: Date; content: ContentType; author: Author; } export interface GetContentsResult> { contents: Content[]; contentType: ContentTypeMetadata; pagination: ContentPagination; } export interface GetContentResult> { content: Content; contentType: ContentTypeMetadata; } export interface CommentPagination { page: number; limit: number | "all"; totalPages: number; totalComments: number; nextPage: number | null; prevPage: number | null; } export interface CommentConfig { enabled: boolean; allowUrls: boolean; allowNested: boolean; reviewType: "AUTO_APPROVE" | "MANUAL_REVIEW" | "AI_REVIEW"; signUpMessage: string | null; } export interface Comment { id: string; author: string; content: string; createdAt: Date; url?: string | null; parent?: { id: string; author: string; content: string; url?: string | null; createdAt: Date; } | null; } export interface GetCommentsResult { comments: Comment[]; pagination: CommentPagination; config: CommentConfig; } export interface CreateCommentInput { slug: string; author: string; email: string; url?: string; content: string; allowEmailUsage: boolean; parentId?: string; } export interface CreateCommentResult { success: boolean; } export const buildWispClient = ({ baseUrl = "https://www.wisp.blog", blogId, }: BuildWispClient) => { return { getPosts: async ({ page, limit, tags, query, }: { page?: number; limit?: number | "all"; tags?: string[]; query?: string; } = {}) => { const queryParams = new URLSearchParams(); if (page) { queryParams.append("page", page.toString()); } if (limit) { queryParams.append("limit", limit.toString()); } if (query) { queryParams.append("query", query.toString()); } if (tags && tags.length) { tags.forEach((tag) => queryParams.append("tag", tag)); } const url = urlJoin( baseUrl, `/api/v1/${blogId}/posts?${queryParams.toString()}`, ); const response = await fetch(url); const data = (await response.json()) as GetPostsResult; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any getPost: async >(slug: string) => { const url = urlJoin(baseUrl, `/api/v1/${blogId}/posts/${slug}`); const response = await fetch(url); const data = (await response.json()) as GetPostResult; return data; }, getRelatedPosts: async ({ slug, limit = 5, }: { slug: string; limit?: number; }) => { const url = urlJoin( baseUrl, `/api/v1/${blogId}/posts/${slug}/related?limit=${limit}`, ); const response = await fetch(url); const data = (await response.json()) as GetRelatedPostsResult; return data; }, getCtas: async ({ slug, limit = 1 }: { slug: string; limit?: number }) => { const url = urlJoin( baseUrl, `/api/v1/${blogId}/posts/${slug}/ctas?limit=${limit}`, ); const response = await fetch(url); const data = (await response.json()) as GetCtasResult; return data; }, getTags: async (page?: number, limit?: number | "all") => { const queryParams = new URLSearchParams(); if (page) { queryParams.append("page", page.toString()); } if (limit) { queryParams.append("limit", limit.toString()); } const url = urlJoin( baseUrl, `/api/v1/${blogId}/tags?${queryParams.toString()}`, ); const response = await fetch(url); const data = (await response.json()) as GetTagsResult; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any getContents: async >({ contentTypeSlug, page, limit, }: { contentTypeSlug: string; page?: number; limit?: number | "all"; }) => { const queryParams = new URLSearchParams(); if (page) { queryParams.append("page", page.toString()); } if (limit) { queryParams.append("limit", limit.toString()); } const url = urlJoin( baseUrl, `/api/v1/${blogId}/contents/${contentTypeSlug}?${queryParams.toString()}`, ); const response = await fetch(url); const data = (await response.json()) as GetContentsResult; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any getContent: async >({ contentTypeSlug, contentSlug, }: { contentTypeSlug: string; contentSlug: string; }) => { const url = urlJoin( baseUrl, `/api/v1/${blogId}/contents/${contentTypeSlug}/${contentSlug}`, ); const response = await fetch(url); const data = (await response.json()) as GetContentResult; return data; }, getComments: async ({ slug, page, limit, }: { slug: string; page?: number; limit?: number | "all"; }) => { const queryParams = new URLSearchParams(); if (page) { queryParams.append("page", page.toString()); } if (limit) { queryParams.append("limit", limit.toString()); } const url = urlJoin( baseUrl, `/api/v1/${blogId}/posts/${slug}/comments?${queryParams.toString()}`, ); const response = await fetch(url); const data = (await response.json()) as GetCommentsResult; return data; }, createComment: async (input: CreateCommentInput) => { const url = urlJoin(baseUrl, `/api/v1/${blogId}/comments`); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(input), }); const data = (await response.json()) as CreateCommentResult; return data; }, }; };