pr_01m47d15m3e54sn21z27rpy5n9/packages/contracts/src/events.ts
| 1 | /** |
| 2 | * Every state change in g1t is published as an event. Services react to |
| 3 | * each other through events rather than direct calls, and the same stream |
| 4 | * feeds timelines, webhooks and automations. |
| 5 | * |
| 6 | * The envelope follows CloudEvents: `type` says what happened, `subject` |
| 7 | * says to what, `data` is the type-specific payload. |
| 8 | */ |
| 9 | export type EventPayloads = { |
| 10 | "repo.created": { repoId: string; namespace: string; name: string; isPrivate: boolean }; |
| 11 | "repo.forked": { repoId: string; sourceRepoId: string; attemptId: string }; |
| 12 | /** `after` is the commit the ref points to once the push has landed. */ |
| 13 | "git.push": { repoId: string; ref: string; after: string }; |
| 14 | "intent.opened": { intentId: string; repoId: string; number: number; title: string }; |
| 15 | "intent.closed": { intentId: string; repoId: string; reason: "shipped" | "withdrawn" }; |
| 16 | "attempt.started": { attemptId: string; intentId: string; repoId: string; agent: string }; |
| 17 | "attempt.updated": { attemptId: string; intentId: string; repoId: string; status: string }; |
| 18 | "attempt.submitted": { attemptId: string; intentId: string; repoId: string }; |
| 19 | "attempt.shipped": { attemptId: string; intentId: string; repoId: string; commit: string }; |
| 20 | "session.appended": { attemptId: string; sessionId: string; count: number }; |
| 21 | }; |
| 22 | |
| 23 | export type EventType = keyof EventPayloads; |
| 24 | |
| 25 | export type G1tEvent<T extends EventType = EventType> = { |
| 26 | [K in T]: { |
| 27 | id: string; |
| 28 | type: K; |
| 29 | /** The service that published it. */ |
| 30 | source: string; |
| 31 | /** Milliseconds since the epoch. */ |
| 32 | time: number; |
| 33 | /** The repo the event concerns, used to scope timelines and deliveries. */ |
| 34 | repoId: string | null; |
| 35 | /** The user or agent that caused it, if any. */ |
| 36 | actor: string | null; |
| 37 | data: EventPayloads[K]; |
| 38 | }; |
| 39 | }[T]; |
| 40 | |
| 41 | /** What a publisher supplies; the bus fills in `id` and `time`. */ |
| 42 | export type NewEvent<T extends EventType = EventType> = { |
| 43 | [K in T]: Omit<G1tEvent<K>, "id" | "time">; |
| 44 | }[T]; |
| 45 | |
| 46 | export type EventQuery = { |
| 47 | repoId?: string; |
| 48 | types?: EventType[]; |
| 49 | /** Return events older than this event id. */ |
| 50 | before?: string; |
| 51 | limit?: number; |
| 52 | }; |
| 53 | |
| 54 | /** The event bus and its durable log. */ |
| 55 | export interface EventsApi { |
| 56 | publish(events: NewEvent[]): Promise<void>; |
| 57 | /** Newest first. */ |
| 58 | list(query: EventQuery): Promise<G1tEvent[]>; |
| 59 | } |
| 60 | |
| 61 | /** Implemented by services that consume events from the bus. */ |
| 62 | export interface EventSubscriber { |
| 63 | onEvents(events: G1tEvent[]): Promise<void>; |
| 64 | } |