@checkstack/integration-backend 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ import type { PluginMetadata } from "@checkstack/common";
2
+ import { toJsonSchema } from "@checkstack/backend-api";
3
+ import type {
4
+ IntegrationProvider,
5
+ RegisteredIntegrationProvider,
6
+ } from "./provider-types";
7
+
8
+ /**
9
+ * Registry for integration providers.
10
+ * Plugins register their providers here to enable webhook delivery to external systems.
11
+ */
12
+ export interface IntegrationProviderRegistry {
13
+ /**
14
+ * Register an integration provider.
15
+ * Called via the extension point during plugin registration.
16
+ */
17
+ register(
18
+ provider: IntegrationProvider<unknown>,
19
+ pluginMetadata: PluginMetadata
20
+ ): void;
21
+
22
+ /** Get all registered providers */
23
+ getProviders(): RegisteredIntegrationProvider<unknown>[];
24
+
25
+ /** Get a specific provider by its fully qualified ID */
26
+ getProvider(
27
+ qualifiedId: string
28
+ ): RegisteredIntegrationProvider<unknown> | undefined;
29
+
30
+ /** Check if a provider is registered */
31
+ hasProvider(qualifiedId: string): boolean;
32
+
33
+ /** Get the JSON Schema for a provider's config */
34
+ getProviderConfigSchema(
35
+ qualifiedId: string
36
+ ): Record<string, unknown> | undefined;
37
+
38
+ /** Get the JSON Schema for a provider's connection config (if any) */
39
+ getProviderConnectionSchema(
40
+ qualifiedId: string
41
+ ): Record<string, unknown> | undefined;
42
+ }
43
+
44
+ /**
45
+ * Create a new integration provider registry instance.
46
+ */
47
+ export function createIntegrationProviderRegistry(): IntegrationProviderRegistry {
48
+ const providers = new Map<string, RegisteredIntegrationProvider<unknown>>();
49
+ const configSchemas = new Map<string, Record<string, unknown>>();
50
+ const connectionSchemas = new Map<string, Record<string, unknown>>();
51
+
52
+ return {
53
+ register(
54
+ provider: IntegrationProvider<unknown>,
55
+ pluginMetadata: PluginMetadata
56
+ ): void {
57
+ const qualifiedId = `${pluginMetadata.pluginId}.${provider.id}`;
58
+
59
+ const registered: RegisteredIntegrationProvider<unknown> = {
60
+ ...provider,
61
+ qualifiedId,
62
+ ownerPluginId: pluginMetadata.pluginId,
63
+ };
64
+
65
+ providers.set(qualifiedId, registered);
66
+
67
+ // Convert the provider's config schema to JSON Schema for UI
68
+ // Uses the platform's toJsonSchema which handles secrets/colors
69
+ const jsonSchema = toJsonSchema(provider.config.schema);
70
+ configSchemas.set(qualifiedId, jsonSchema);
71
+
72
+ // Also convert connection schema if present
73
+ if (provider.connectionSchema) {
74
+ const connectionJsonSchema = toJsonSchema(
75
+ provider.connectionSchema.schema
76
+ );
77
+ connectionSchemas.set(qualifiedId, connectionJsonSchema);
78
+ }
79
+ },
80
+
81
+ getProviders(): RegisteredIntegrationProvider<unknown>[] {
82
+ return [...providers.values()];
83
+ },
84
+
85
+ getProvider(
86
+ qualifiedId: string
87
+ ): RegisteredIntegrationProvider<unknown> | undefined {
88
+ return providers.get(qualifiedId);
89
+ },
90
+
91
+ hasProvider(qualifiedId: string): boolean {
92
+ return providers.has(qualifiedId);
93
+ },
94
+
95
+ getProviderConfigSchema(
96
+ qualifiedId: string
97
+ ): Record<string, unknown> | undefined {
98
+ return configSchemas.get(qualifiedId);
99
+ },
100
+
101
+ getProviderConnectionSchema(
102
+ qualifiedId: string
103
+ ): Record<string, unknown> | undefined {
104
+ return connectionSchemas.get(qualifiedId);
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Integration Provider Types
3
+ *
4
+ * These types define the contract for integration provider plugins.
5
+ * All backend-only types live here - frontend uses Zod schemas from integration-common.
6
+ */
7
+ import { z } from "zod";
8
+ import type { Versioned, Logger, Hook } from "@checkstack/backend-api";
9
+ import type { LucideIconName } from "@checkstack/common";
10
+
11
+ // =============================================================================
12
+ // Integration Event Definition Types
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Metadata for registering a hook as an integration event.
17
+ * Plugins use this to expose their hooks for external webhook subscriptions.
18
+ */
19
+ export interface IntegrationEventDefinition<T = unknown> {
20
+ /** The hook to expose (from the owning plugin) */
21
+ hook: Hook<T>;
22
+
23
+ /** Human-readable name for the UI */
24
+ displayName: string;
25
+
26
+ /** Description of when this event fires */
27
+ description?: string;
28
+
29
+ /** Category for UI grouping (e.g., "Health", "Incidents", "Maintenance") */
30
+ category?: string;
31
+
32
+ /** Zod schema for the payload (used for UI preview and validation) */
33
+ payloadSchema: z.ZodType<T>;
34
+
35
+ /**
36
+ * Optional: Transform hook payload before sending to webhooks.
37
+ * Use this to enrich the payload with additional context or
38
+ * redact sensitive fields.
39
+ */
40
+ transformPayload?: (payload: T) => Record<string, unknown>;
41
+ }
42
+
43
+ // =============================================================================
44
+ // Integration Provider Types
45
+ // =============================================================================
46
+
47
+ /**
48
+ * Context passed to the provider's deliver() method.
49
+ */
50
+ export interface IntegrationDeliveryContext<TConfig = unknown> {
51
+ event: {
52
+ /** Fully qualified event ID */
53
+ eventId: string;
54
+ /** Event payload (possibly transformed) */
55
+ payload: Record<string, unknown>;
56
+ /** ISO timestamp when the event was emitted */
57
+ timestamp: string;
58
+ /** Unique ID for this delivery attempt */
59
+ deliveryId: string;
60
+ };
61
+ subscription: {
62
+ /** Subscription ID */
63
+ id: string;
64
+ /** Subscription name */
65
+ name: string;
66
+ };
67
+ /** Provider-specific configuration */
68
+ providerConfig: TConfig;
69
+ /** Scoped logger for delivery tracing */
70
+ logger: Logger;
71
+ /**
72
+ * Get connection credentials by ID (for providers with connectionSchema).
73
+ * Only available when provider has a connectionSchema defined.
74
+ */
75
+ getConnectionWithCredentials?: (
76
+ connectionId: string
77
+ ) => Promise<{ config: Record<string, unknown> } | undefined>;
78
+ }
79
+
80
+ /**
81
+ * Result of a provider delivery attempt.
82
+ */
83
+ export interface IntegrationDeliveryResult {
84
+ success: boolean;
85
+ /** External ID returned by the target system (e.g., Jira issue key) */
86
+ externalId?: string;
87
+ /** Error message if delivery failed */
88
+ error?: string;
89
+ /** Milliseconds to wait before retrying (if applicable) */
90
+ retryAfterMs?: number;
91
+ }
92
+
93
+ /**
94
+ * Result of testing a provider connection.
95
+ */
96
+ export interface TestConnectionResult {
97
+ success: boolean;
98
+ message?: string;
99
+ }
100
+
101
+ /**
102
+ * Documentation that helps users implement their endpoint for this provider.
103
+ */
104
+ export interface ProviderDocumentation {
105
+ /** Brief setup instructions (rendered as markdown) */
106
+ setupGuide?: string;
107
+ /** Example request body (JSON string for syntax highlighting) */
108
+ examplePayload?: string;
109
+ /** HTTP headers that will be sent with each request */
110
+ headers?: Array<{ name: string; description: string }>;
111
+ /** Link to external documentation */
112
+ externalDocsUrl?: string;
113
+ }
114
+
115
+ /**
116
+ * Option returned by getConnectionOptions for dynamic dropdowns.
117
+ */
118
+ export interface ConnectionOption {
119
+ value: string;
120
+ label: string;
121
+ description?: string;
122
+ }
123
+
124
+ /**
125
+ * Parameters for getConnectionOptions method.
126
+ */
127
+ export interface GetConnectionOptionsParams {
128
+ /** The connection ID to use for fetching options */
129
+ connectionId: string;
130
+ /** Name of the resolver (matches x-options-resolver in schema) */
131
+ resolverName: string;
132
+ /** Current form values for dependent fields */
133
+ context: Record<string, unknown>;
134
+ /** Logger for logging */
135
+ logger: Logger;
136
+ /**
137
+ * Get connection credentials by ID.
138
+ * Provided by the integration backend for providers to access connection config.
139
+ */
140
+ getConnectionWithCredentials: (
141
+ connectionId: string
142
+ ) => Promise<{ config: Record<string, unknown> } | undefined>;
143
+ }
144
+
145
+ /**
146
+ * Integration provider definition.
147
+ * Providers define how to deliver events to specific external systems.
148
+ *
149
+ * @template TConfig - Per-subscription configuration type
150
+ * @template TConnection - Site-wide connection configuration type (optional)
151
+ */
152
+ export interface IntegrationProvider<
153
+ TConfig = unknown,
154
+ TConnection = undefined
155
+ > {
156
+ /** Local identifier, namespaced on registration to {pluginId}.{id} */
157
+ id: string;
158
+
159
+ /** Display name for UI */
160
+ displayName: string;
161
+
162
+ /** Description of what this provider does */
163
+ description?: string;
164
+
165
+ /** Lucide icon name in PascalCase (e.g., 'Webhook') */
166
+ icon?: LucideIconName;
167
+
168
+ /** Per-subscription configuration schema */
169
+ config: Versioned<TConfig>;
170
+
171
+ /**
172
+ * Optional site-wide connection schema.
173
+ * When provided, the platform will:
174
+ * - Store connections centrally via ConfigService
175
+ * - Show a "Connections" management UI
176
+ * - Add a connection dropdown to subscription config
177
+ */
178
+ connectionSchema?: Versioned<TConnection>;
179
+
180
+ /**
181
+ * Events this provider can handle.
182
+ * If undefined, provider accepts all events.
183
+ * Event IDs are fully qualified: {pluginId}.{hookId}
184
+ */
185
+ supportedEvents?: string[];
186
+
187
+ /**
188
+ * Optional documentation to help users configure their endpoints.
189
+ * Displayed in the UI when creating/editing subscriptions.
190
+ */
191
+ documentation?: ProviderDocumentation;
192
+
193
+ /**
194
+ * Transform and deliver the event to the external system.
195
+ */
196
+ deliver(
197
+ context: IntegrationDeliveryContext<TConfig>
198
+ ): Promise<IntegrationDeliveryResult>;
199
+
200
+ /**
201
+ * Optional: Test the connection configuration.
202
+ * Called when admin clicks "Test Connection" in the connections UI.
203
+ * Only applicable when connectionSchema is defined.
204
+ */
205
+ testConnection?(config: TConnection): Promise<TestConnectionResult>;
206
+
207
+ /**
208
+ * Optional: Fetch dynamic options for cascading dropdowns.
209
+ * Called when subscription config has fields with x-options-resolver.
210
+ * Only applicable when connectionSchema is defined.
211
+ */
212
+ getConnectionOptions?(
213
+ params: GetConnectionOptionsParams
214
+ ): Promise<ConnectionOption[]>;
215
+ }
216
+
217
+ /**
218
+ * Registered provider with full namespace information.
219
+ */
220
+ export interface RegisteredIntegrationProvider<
221
+ TConfig = unknown,
222
+ TConnection = unknown
223
+ > extends IntegrationProvider<TConfig, TConnection> {
224
+ /** Fully qualified ID: {pluginId}.{id} */
225
+ qualifiedId: string;
226
+
227
+ /** Plugin that registered this provider */
228
+ ownerPluginId: string;
229
+ }
230
+
231
+ /**
232
+ * Registered integration event with full namespace information.
233
+ */
234
+ export interface RegisteredIntegrationEvent<T = unknown> {
235
+ /** Fully qualified event ID: {pluginId}.{hookId} */
236
+ eventId: string;
237
+
238
+ /** Original hook reference */
239
+ hook: Hook<T>;
240
+
241
+ /** Plugin that registered this event */
242
+ ownerPluginId: string;
243
+
244
+ /** UI metadata */
245
+ displayName: string;
246
+ description?: string;
247
+ category?: string;
248
+
249
+ /** JSON Schema for payload (derived from Zod) */
250
+ payloadJsonSchema: Record<string, unknown>;
251
+
252
+ /** Original Zod schema */
253
+ payloadSchema: z.ZodType<T>;
254
+
255
+ /** Optional payload transformer */
256
+ transformPayload?: (payload: T) => Record<string, unknown>;
257
+ }