import { authedGet, authedPost, type AppviewContext } from "./appview"; import type { XrpcRequestInit } from "./client"; // canonical event names the appview dispatches on (models.WebhookEvent*). the // values are the wire format; labels are a UI concern. export const WEBHOOK_EVENTS = [ { id: "push", label: "Push events" }, { id: "repository:renamed", label: "Repository renamed" }, { id: "pull_request:created", label: "Pull request opened" }, { id: "pull_request:resubmitted", label: "Pull request resubmitted" }, { id: "pull_request:merged", label: "Pull request merged" }, { id: "pull_request:closed", label: "Pull request closed" }, { id: "pull_request:reopened", label: "Pull request reopened" } ] as const; export type WebhookEvent = (typeof WEBHOOK_EVENTS)[number]["id"]; // mirrors org.tangled.temp.repo.listWebhooks#webhook export interface Webhook { id: number; url: string; active: boolean; events: string[]; createdAt: string; updatedAt?: string; } // mirrors org.tangled.temp.repo.listWebhookDeliveries#delivery export interface WebhookDelivery { id: number; deliveryId: string; event: string; url: string; success: boolean; createdAt: string; requestBody?: string; responseBody?: string; responseCode?: number; } export interface CreateWebhookInput { url: string; events: string[]; secret?: string; active?: boolean; } export interface UpdateWebhookInput { url?: string; events?: string[]; secret?: string; active?: boolean; } const LIST = "org.tangled.temp.repo.listWebhooks"; const CREATE = "org.tangled.temp.repo.createWebhook"; const UPDATE = "org.tangled.temp.repo.updateWebhook"; const DELETE = "org.tangled.temp.repo.deleteWebhook"; const TOGGLE = "org.tangled.temp.repo.toggleWebhook"; const LIST_DELIVERIES = "org.tangled.temp.repo.listWebhookDeliveries"; const RETRY_DELIVERY = "org.tangled.temp.repo.retryWebhookDelivery"; export const listWebhooks = async ( ctx: AppviewContext, repoDid: string, init?: XrpcRequestInit ): Promise => { const res = await authedGet<{ webhooks?: Webhook[] }>(ctx, LIST, { repoDid }, init); return res.webhooks ?? []; }; export const createWebhook = async ( ctx: AppviewContext, repoDid: string, input: CreateWebhookInput, init?: XrpcRequestInit ): Promise => { const res = await authedPost<{ id: number }>(ctx, CREATE, { repoDid, ...input }, init); return res?.id; }; export const updateWebhook = ( ctx: AppviewContext, repoDid: string, id: number, patch: UpdateWebhookInput, init?: XrpcRequestInit ): Promise => authedPost(ctx, UPDATE, { repoDid, id, ...patch }, init).then(() => undefined); export const deleteWebhook = ( ctx: AppviewContext, repoDid: string, id: number, init?: XrpcRequestInit ): Promise => authedPost(ctx, DELETE, { repoDid, id }, init).then(() => undefined); // flips active state server-side; returns the new value. export const toggleWebhook = async ( ctx: AppviewContext, repoDid: string, id: number, init?: XrpcRequestInit ): Promise => { const res = await authedPost<{ active: boolean }>(ctx, TOGGLE, { repoDid, id }, init); return res?.active; }; export const listWebhookDeliveries = async ( ctx: AppviewContext, repoDid: string, id: number, limit?: number, init?: XrpcRequestInit ): Promise => { const res = await authedGet<{ deliveries?: WebhookDelivery[] }>( ctx, LIST_DELIVERIES, { repoDid, id, limit }, init ); return res.deliveries ?? []; }; export const retryWebhookDelivery = ( ctx: AppviewContext, repoDid: string, webhookId: number, deliveryId: string, init?: XrpcRequestInit ): Promise => authedPost(ctx, RETRY_DELIVERY, { repoDid, webhookId, deliveryId }, init).then(() => undefined);