@checkstack/integration-backend 0.1.30 → 0.3.0

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.
@@ -1,314 +1,84 @@
1
- import { describe, it, expect, beforeEach } from "bun:test";
1
+ /**
2
+ * Provider registry behaviour tests — scoped to connection-management
3
+ * after the Automation Platform migration. Subscription / delivery
4
+ * concerns moved to the trigger / action registries on the automation
5
+ * side; the integration registry now only owns connection schemas.
6
+ */
7
+ import { describe, expect, it, beforeEach } from "bun:test";
2
8
  import { z } from "zod";
9
+ import { Versioned, configString } from "@checkstack/backend-api";
3
10
  import {
4
11
  createIntegrationProviderRegistry,
5
12
  type IntegrationProviderRegistry,
6
13
  } from "./provider-registry";
7
14
  import type { IntegrationProvider } from "./provider-types";
8
- import { Versioned } from "@checkstack/backend-api";
9
15
 
10
- /**
11
- * Unit tests for IntegrationProviderRegistry.
12
- *
13
- * Tests cover:
14
- * - Provider registration with proper namespacing
15
- * - Provider retrieval by qualified ID
16
- * - Config schema JSON conversion
17
- */
16
+ const testPlugin = { pluginId: "test-plugin" } as const;
18
17
 
19
- // Test plugin metadata
20
- const testPluginMetadata = {
21
- pluginId: "test-plugin",
22
- displayName: "Test Plugin",
23
- } as const;
24
-
25
- // Test config schemas
26
- const webhookConfigSchema = z.object({
27
- url: z.string().url(),
28
- method: z.enum(["GET", "POST"]),
29
- timeout: z.number().default(5000),
18
+ const sampleConnectionSchema = z.object({
19
+ apiKey: configString({ "x-secret": true }).describe("API key"),
30
20
  });
31
21
 
32
- const slackConfigSchema = z.object({
33
- webhookUrl: z.string().url(),
34
- channel: z.string(),
35
- });
22
+ type SampleConnection = z.infer<typeof sampleConnectionSchema>;
36
23
 
37
- // Create test providers
38
- function createTestProvider(
39
- id: string,
40
- schema: z.ZodType<unknown>
41
- ): IntegrationProvider<unknown> {
42
- return {
43
- id,
44
- displayName: `${id.charAt(0).toUpperCase()}${id.slice(1)} Provider`,
45
- description: `Deliver events via ${id}`,
46
- icon: "Webhook",
47
- config: new Versioned({
48
- version: 1,
49
- schema,
50
- }),
51
- deliver: async () => ({ success: true }),
52
- };
53
- }
24
+ const sampleProvider: IntegrationProvider<SampleConnection> = {
25
+ id: "sample",
26
+ displayName: "Sample",
27
+ description: "Sample provider for tests",
28
+ icon: "Webhook",
29
+ connectionSchema: new Versioned({
30
+ version: 1,
31
+ schema: sampleConnectionSchema,
32
+ }),
33
+ };
54
34
 
55
35
  describe("IntegrationProviderRegistry", () => {
56
36
  let registry: IntegrationProviderRegistry;
57
-
58
37
  beforeEach(() => {
59
38
  registry = createIntegrationProviderRegistry();
60
39
  });
61
40
 
62
- // ─────────────────────────────────────────────────────────────────────────
63
- // Provider Registration
64
- // ─────────────────────────────────────────────────────────────────────────
65
-
66
- describe("register", () => {
67
- it("registers a provider with a fully qualified ID", () => {
68
- const provider = createTestProvider("webhook", webhookConfigSchema);
69
-
70
- registry.register(provider, testPluginMetadata);
71
-
72
- expect(registry.hasProvider("test-plugin.webhook")).toBe(true);
73
- });
74
-
75
- it("generates correct qualified ID", () => {
76
- const provider = createTestProvider("webhook", webhookConfigSchema);
77
-
78
- registry.register(provider, testPluginMetadata);
79
-
80
- const registered = registry.getProvider("test-plugin.webhook");
81
- expect(registered?.qualifiedId).toBe("test-plugin.webhook");
82
- expect(registered?.ownerPluginId).toBe("test-plugin");
83
- });
84
-
85
- it("preserves provider metadata", () => {
86
- const provider: IntegrationProvider<unknown> = {
87
- id: "custom",
88
- displayName: "Custom Provider",
89
- description: "A custom provider for testing",
90
- icon: "Cog",
91
- config: new Versioned({ version: 1, schema: webhookConfigSchema }),
92
- deliver: async () => ({ success: true }),
93
- };
94
-
95
- registry.register(provider, testPluginMetadata);
96
-
97
- const registered = registry.getProvider("test-plugin.custom");
98
- expect(registered?.displayName).toBe("Custom Provider");
99
- expect(registered?.description).toBe("A custom provider for testing");
100
- expect(registered?.icon).toBe("Cog");
101
- });
102
-
103
- it("preserves deliver function", () => {
104
- const deliverFn = async () => ({ success: true, externalId: "ext-123" });
105
- const provider: IntegrationProvider<unknown> = {
106
- id: "webhook",
107
- displayName: "Webhook",
108
- config: new Versioned({ version: 1, schema: webhookConfigSchema }),
109
- deliver: deliverFn,
110
- };
111
-
112
- registry.register(provider, testPluginMetadata);
113
-
114
- const registered = registry.getProvider("test-plugin.webhook");
115
- expect(registered?.deliver).toBeDefined();
116
- });
117
-
118
- it("preserves testConnection function if provided", () => {
119
- const provider: IntegrationProvider<unknown> = {
120
- id: "webhook",
121
- displayName: "Webhook",
122
- config: new Versioned({ version: 1, schema: webhookConfigSchema }),
123
- deliver: async () => ({ success: true }),
124
- testConnection: async () => ({ success: true, message: "OK" }),
125
- };
126
-
127
- registry.register(provider, testPluginMetadata);
128
-
129
- const registered = registry.getProvider("test-plugin.webhook");
130
- expect(registered?.testConnection).toBeDefined();
131
- });
132
- });
133
-
134
- // ─────────────────────────────────────────────────────────────────────────
135
- // Provider Retrieval
136
- // ─────────────────────────────────────────────────────────────────────────
137
-
138
- describe("getProviders", () => {
139
- it("returns empty array when no providers registered", () => {
140
- expect(registry.getProviders()).toEqual([]);
141
- });
142
-
143
- it("returns all registered providers", () => {
144
- registry.register(
145
- createTestProvider("webhook", webhookConfigSchema),
146
- testPluginMetadata
147
- );
148
- registry.register(
149
- createTestProvider("slack", slackConfigSchema),
150
- testPluginMetadata
151
- );
152
-
153
- const providers = registry.getProviders();
154
- expect(providers.length).toBe(2);
155
- expect(providers.map((p) => p.id).sort()).toEqual(["slack", "webhook"]);
156
- });
157
- });
158
-
159
- describe("getProvider", () => {
160
- it("returns undefined for non-existent provider", () => {
161
- expect(registry.getProvider("non-existent.provider")).toBeUndefined();
162
- });
163
-
164
- it("returns provider by qualified ID", () => {
165
- registry.register(
166
- createTestProvider("webhook", webhookConfigSchema),
167
- testPluginMetadata
168
- );
169
-
170
- const provider = registry.getProvider("test-plugin.webhook");
171
- expect(provider?.displayName).toBe("Webhook Provider");
172
- });
41
+ it("namespaces ids by plugin", () => {
42
+ registry.register(sampleProvider, testPlugin);
43
+ const registered = registry.getProvider("test-plugin.sample");
44
+ expect(registered).toBeDefined();
45
+ expect(registered?.qualifiedId).toBe("test-plugin.sample");
46
+ expect(registered?.ownerPluginId).toBe("test-plugin");
173
47
  });
174
48
 
175
- describe("hasProvider", () => {
176
- it("returns false for non-existent provider", () => {
177
- expect(registry.hasProvider("non-existent.provider")).toBe(false);
178
- });
179
-
180
- it("returns true for registered provider", () => {
181
- registry.register(
182
- createTestProvider("webhook", webhookConfigSchema),
183
- testPluginMetadata
184
- );
185
-
186
- expect(registry.hasProvider("test-plugin.webhook")).toBe(true);
187
- });
49
+ it("returns all registered providers", () => {
50
+ registry.register(sampleProvider, testPlugin);
51
+ registry.register(
52
+ { ...sampleProvider, id: "other" } as IntegrationProvider<SampleConnection>,
53
+ testPlugin,
54
+ );
55
+ expect(registry.getProviders()).toHaveLength(2);
188
56
  });
189
57
 
190
- // ─────────────────────────────────────────────────────────────────────────
191
- // Config Schema
192
- // ─────────────────────────────────────────────────────────────────────────
193
-
194
- describe("getProviderConfigSchema", () => {
195
- it("returns undefined for non-existent provider", () => {
196
- expect(
197
- registry.getProviderConfigSchema("non-existent.provider")
198
- ).toBeUndefined();
199
- });
200
-
201
- it("returns JSON Schema for provider config", () => {
202
- registry.register(
203
- createTestProvider("webhook", webhookConfigSchema),
204
- testPluginMetadata
205
- );
206
-
207
- const schema = registry.getProviderConfigSchema("test-plugin.webhook");
208
- expect(schema).toBeDefined();
209
- expect(typeof schema).toBe("object");
210
- expect(schema?.type).toBe("object");
211
- });
212
-
213
- it("JSON Schema includes property definitions", () => {
214
- registry.register(
215
- createTestProvider("webhook", webhookConfigSchema),
216
- testPluginMetadata
217
- );
218
-
219
- const schema = registry.getProviderConfigSchema("test-plugin.webhook");
220
- const properties = schema?.properties as Record<string, unknown>;
221
-
222
- expect(properties).toBeDefined();
223
- expect(properties.url).toBeDefined();
224
- expect(properties.method).toBeDefined();
225
- expect(properties.timeout).toBeDefined();
226
- });
58
+ it("reports presence via hasProvider", () => {
59
+ registry.register(sampleProvider, testPlugin);
60
+ expect(registry.hasProvider("test-plugin.sample")).toBe(true);
61
+ expect(registry.hasProvider("test-plugin.missing")).toBe(false);
227
62
  });
228
63
 
229
- // ─────────────────────────────────────────────────────────────────────────
230
- // Multi-Plugin Registration
231
- // ─────────────────────────────────────────────────────────────────────────
232
-
233
- describe("multi-plugin registration", () => {
234
- it("handles providers from multiple plugins", () => {
235
- const plugin1 = { pluginId: "plugin-1" } as const;
236
- const plugin2 = { pluginId: "plugin-2" } as const;
237
-
238
- registry.register(
239
- createTestProvider("webhook", webhookConfigSchema),
240
- plugin1
241
- );
242
- registry.register(
243
- createTestProvider("webhook", webhookConfigSchema),
244
- plugin2
245
- );
246
-
247
- expect(registry.hasProvider("plugin-1.webhook")).toBe(true);
248
- expect(registry.hasProvider("plugin-2.webhook")).toBe(true);
249
-
250
- const providers = registry.getProviders();
251
- expect(providers.length).toBe(2);
252
- });
253
-
254
- it("correctly namespaces providers by plugin", () => {
255
- const plugin1 = { pluginId: "integration-webhook" } as const;
256
- const plugin2 = { pluginId: "integration-slack" } as const;
257
-
258
- registry.register(
259
- {
260
- ...createTestProvider("default", webhookConfigSchema),
261
- displayName: "Webhook",
262
- },
263
- plugin1
264
- );
265
- registry.register(
266
- {
267
- ...createTestProvider("default", slackConfigSchema),
268
- displayName: "Slack",
269
- },
270
- plugin2
271
- );
272
-
273
- expect(
274
- registry.getProvider("integration-webhook.default")?.displayName
275
- ).toBe("Webhook");
276
- expect(
277
- registry.getProvider("integration-slack.default")?.displayName
278
- ).toBe("Slack");
279
- });
64
+ it("exposes the connection JSON schema when present", () => {
65
+ registry.register(sampleProvider, testPlugin);
66
+ const schema = registry.getProviderConnectionSchema("test-plugin.sample");
67
+ expect(schema).toBeDefined();
68
+ expect((schema as Record<string, unknown>).type).toBe("object");
280
69
  });
281
70
 
282
- // ─────────────────────────────────────────────────────────────────────────
283
- // Supported Events
284
- // ─────────────────────────────────────────────────────────────────────────
285
-
286
- describe("supportedEvents", () => {
287
- it("preserves supportedEvents array", () => {
288
- const provider: IntegrationProvider<unknown> = {
289
- id: "limited",
290
- displayName: "Limited Provider",
291
- config: new Versioned({ version: 1, schema: webhookConfigSchema }),
292
- supportedEvents: ["incident.created", "incident.resolved"],
293
- deliver: async () => ({ success: true }),
294
- };
295
-
296
- registry.register(provider, testPluginMetadata);
297
-
298
- const registered = registry.getProvider("test-plugin.limited");
299
- expect(registered?.supportedEvents).toEqual([
300
- "incident.created",
301
- "incident.resolved",
302
- ]);
303
- });
304
-
305
- it("handles provider with no supportedEvents (accepts all)", () => {
306
- const provider = createTestProvider("webhook", webhookConfigSchema);
307
-
308
- registry.register(provider, testPluginMetadata);
309
-
310
- const registered = registry.getProvider("test-plugin.webhook");
311
- expect(registered?.supportedEvents).toBeUndefined();
312
- });
71
+ it("returns undefined for providers without a connection schema", () => {
72
+ const minimal: IntegrationProvider<undefined> = {
73
+ id: "minimal",
74
+ displayName: "Minimal",
75
+ };
76
+ registry.register(
77
+ minimal as IntegrationProvider<unknown>,
78
+ testPlugin,
79
+ );
80
+ expect(
81
+ registry.getProviderConnectionSchema("test-plugin.minimal"),
82
+ ).toBeUndefined();
313
83
  });
314
84
  });
@@ -6,8 +6,11 @@ import type {
6
6
  } from "./provider-types";
7
7
 
8
8
  /**
9
- * Registry for integration providers.
10
- * Plugins register their providers here to enable webhook delivery to external systems.
9
+ * Registry for integration providers — now scoped to connection
10
+ * management only. Per-subscription `config` + `deliver` moved to the
11
+ * Automation platform's `ActionDefinition` (see
12
+ * `@checkstack/automation-backend`); the legacy subscription system is
13
+ * gone.
11
14
  */
12
15
  export interface IntegrationProviderRegistry {
13
16
  /**
@@ -30,11 +33,6 @@ export interface IntegrationProviderRegistry {
30
33
  /** Check if a provider is registered */
31
34
  hasProvider(qualifiedId: string): boolean;
32
35
 
33
- /** Get the JSON Schema for a provider's config */
34
- getProviderConfigSchema(
35
- qualifiedId: string
36
- ): Record<string, unknown> | undefined;
37
-
38
36
  /** Get the JSON Schema for a provider's connection config (if any) */
39
37
  getProviderConnectionSchema(
40
38
  qualifiedId: string
@@ -46,7 +44,6 @@ export interface IntegrationProviderRegistry {
46
44
  */
47
45
  export function createIntegrationProviderRegistry(): IntegrationProviderRegistry {
48
46
  const providers = new Map<string, RegisteredIntegrationProvider<unknown>>();
49
- const configSchemas = new Map<string, Record<string, unknown>>();
50
47
  const connectionSchemas = new Map<string, Record<string, unknown>>();
51
48
 
52
49
  return {
@@ -64,12 +61,6 @@ export function createIntegrationProviderRegistry(): IntegrationProviderRegistry
64
61
 
65
62
  providers.set(qualifiedId, registered);
66
63
 
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
64
  if (provider.connectionSchema) {
74
65
  const connectionJsonSchema = toJsonSchema(
75
66
  provider.connectionSchema.schema
@@ -92,12 +83,6 @@ export function createIntegrationProviderRegistry(): IntegrationProviderRegistry
92
83
  return providers.has(qualifiedId);
93
84
  },
94
85
 
95
- getProviderConfigSchema(
96
- qualifiedId: string
97
- ): Record<string, unknown> | undefined {
98
- return configSchemas.get(qualifiedId);
99
- },
100
-
101
86
  getProviderConnectionSchema(
102
87
  qualifiedId: string
103
88
  ): Record<string, unknown> | undefined {
@@ -1,95 +1,11 @@
1
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.
2
+ * Integration Provider Types — connection-only after the Automation
3
+ * Platform migration. Per-event delivery context types are gone; the
4
+ * Automation engine's `ActionExecutionContext` replaces them.
6
5
  */
7
- import { z } from "zod";
8
- import type { Versioned, Logger, Hook } from "@checkstack/backend-api";
6
+ import type { Versioned, Logger } from "@checkstack/backend-api";
9
7
  import type { LucideIconName } from "@checkstack/common";
10
8
 
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
9
  /**
94
10
  * Result of testing a provider connection.
95
11
  */
@@ -144,15 +60,19 @@ export interface GetConnectionOptionsParams {
144
60
 
145
61
  /**
146
62
  * Integration provider definition.
147
- * Providers define how to deliver events to specific external systems.
63
+ * Connection-provider definition. Plugins register one per external
64
+ * system to expose a connection schema, test endpoint, and dynamic
65
+ * dropdown resolvers used by the Automation editor's config forms.
148
66
  *
149
- * @template TConfig - Per-subscription configuration type
150
- * @template TConnection - Site-wide connection configuration type (optional)
67
+ * The legacy `config` (per-subscription) + `deliver` fields are gone:
68
+ * those responsibilities moved to the Automation platform's
69
+ * `ActionDefinition` (see `@checkstack/automation-backend`). Existing
70
+ * subscriptions are migrated to automations on boot — see
71
+ * `core/automation-backend/src/migration/`.
72
+ *
73
+ * @template TConnection - Site-wide connection configuration type
151
74
  */
152
- export interface IntegrationProvider<
153
- TConfig = unknown,
154
- TConnection = undefined
155
- > {
75
+ export interface IntegrationProvider<TConnection = undefined> {
156
76
  /** Local identifier, namespaced on registration to {pluginId}.{id} */
157
77
  id: string;
158
78
 
@@ -165,38 +85,21 @@ export interface IntegrationProvider<
165
85
  /** Lucide icon name in PascalCase (e.g., 'Webhook') */
166
86
  icon?: LucideIconName;
167
87
 
168
- /** Per-subscription configuration schema */
169
- config: Versioned<TConfig>;
170
-
171
88
  /**
172
89
  * Optional site-wide connection schema.
173
90
  * When provided, the platform will:
174
91
  * - Store connections centrally via ConfigService
175
92
  * - Show a "Connections" management UI
176
- * - Add a connection dropdown to subscription config
93
+ * - Make connections selectable in automation action config forms
177
94
  */
178
95
  connectionSchema?: Versioned<TConnection>;
179
96
 
180
97
  /**
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.
98
+ * Optional documentation to help users configure connections.
99
+ * Displayed on the connections page.
190
100
  */
191
101
  documentation?: ProviderDocumentation;
192
102
 
193
- /**
194
- * Transform and deliver the event to the external system.
195
- */
196
- deliver(
197
- context: IntegrationDeliveryContext<TConfig>
198
- ): Promise<IntegrationDeliveryResult>;
199
-
200
103
  /**
201
104
  * Optional: Test the connection configuration.
202
105
  * Called when admin clicks "Test Connection" in the connections UI.
@@ -205,9 +108,9 @@ export interface IntegrationProvider<
205
108
  testConnection?(config: TConnection): Promise<TestConnectionResult>;
206
109
 
207
110
  /**
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.
111
+ * Optional: Fetch dynamic options for cascading dropdowns inside
112
+ * automation action config forms. Resolver names are declared via
113
+ * `configString({ "x-options-resolver": "name" })` in action configs.
211
114
  */
212
115
  getConnectionOptions?(
213
116
  params: GetConnectionOptionsParams
@@ -217,10 +120,8 @@ export interface IntegrationProvider<
217
120
  /**
218
121
  * Registered provider with full namespace information.
219
122
  */
220
- export interface RegisteredIntegrationProvider<
221
- TConfig = unknown,
222
- TConnection = unknown
223
- > extends IntegrationProvider<TConfig, TConnection> {
123
+ export interface RegisteredIntegrationProvider<TConnection = unknown>
124
+ extends IntegrationProvider<TConnection> {
224
125
  /** Fully qualified ID: {pluginId}.{id} */
225
126
  qualifiedId: string;
226
127
 
@@ -228,30 +129,3 @@ export interface RegisteredIntegrationProvider<
228
129
  ownerPluginId: string;
229
130
  }
230
131
 
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
- }