@automate.ax/integration-contracts 0.82.0 → 0.83.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.
@@ -0,0 +1,109 @@
1
+ import type { JSONValue } from "convex/values";
2
+ import * as z from "zod";
3
+ import { type ConvexValue } from "./schemas.js";
4
+ export declare const CONVEX_DEPLOYMENT_URL_SCHEMA: z.ZodURL;
5
+ export declare const CONVEX_SECRET_SCHEMA: z.ZodUnion<readonly [z.ZodObject<{
6
+ accessToken: z.ZodString;
7
+ projectId: z.ZodString;
8
+ }, z.core.$strip>, z.ZodObject<{
9
+ deployKey: z.ZodString;
10
+ deploymentUrl: z.ZodURL;
11
+ webhookSecret: z.ZodString;
12
+ }, z.core.$strip>]>;
13
+ export interface ConvexDeployment {
14
+ deploymentUrl: string;
15
+ reference: string;
16
+ token: string;
17
+ }
18
+ /**
19
+ * Resolves the authenticated Convex deployment represented by an account.
20
+ *
21
+ * @param secret - Persisted Convex account secret.
22
+ * @param deployment - Default alias or explicit deployment reference.
23
+ */
24
+ export declare function resolveConvexDeployment(secret: unknown, deployment?: string): Promise<ConvexDeployment>;
25
+ /**
26
+ * Creates a small authenticated client for named Convex functions.
27
+ *
28
+ * @param secret - Persisted Convex account secret.
29
+ * @param deployment - Default alias or explicit deployment reference.
30
+ */
31
+ export declare function getConvexClient(secret: unknown, deployment?: string): Promise<{
32
+ action: (functionName: string, arguments_?: Record<string, ConvexValue | undefined>) => Promise<ConvexValue>;
33
+ mutation: (functionName: string, arguments_?: Record<string, ConvexValue | undefined>) => Promise<ConvexValue>;
34
+ query: (functionName: string, arguments_?: Record<string, ConvexValue | undefined>) => Promise<ConvexValue>;
35
+ }>;
36
+ /**
37
+ * Runs a named Convex function through the authenticated deployment client.
38
+ *
39
+ * @param kind - Convex function kind.
40
+ * @param secret - Persisted Convex account secret.
41
+ * @param input - Function path, arguments, and deployment selection.
42
+ * @param input.arguments - Convex arguments keyed by parameter name.
43
+ * @param input.deployment - Default alias or explicit deployment reference.
44
+ * @param input.functionName - Exported Convex function path.
45
+ */
46
+ export declare function runConvexFunction(kind: "action" | "mutation" | "query", secret: unknown, input: {
47
+ arguments?: Record<string, ConvexValue | undefined>;
48
+ deployment?: string;
49
+ functionName: string;
50
+ }): Promise<ConvexValue>;
51
+ /**
52
+ * Returns the deployment API token stored by either connection method.
53
+ *
54
+ * @param secret - Persisted Convex account secret.
55
+ */
56
+ export declare function getConvexDeploymentToken(secret: unknown): string;
57
+ /**
58
+ * Decodes Convex's tagged JSON representation into a native Convex value.
59
+ *
60
+ * @param value - Tagged JSON returned by a Convex HTTP API.
61
+ */
62
+ export declare function decodeConvexValue(value: JSONValue): ConvexValue;
63
+ /**
64
+ * Encodes a native Convex value into Convex's tagged JSON representation.
65
+ *
66
+ * @param value - Native Convex value to encode.
67
+ */
68
+ export declare function encodeConvexValue(value: ConvexValue): JSONValue;
69
+ /**
70
+ * Checks a deployment-key connection and returns provider-owned identity.
71
+ *
72
+ * @param deploymentUrl - Connected Convex deployment URL.
73
+ * @param token - Convex deploy key.
74
+ */
75
+ export declare function getConvexDeploymentInfo(deploymentUrl: string, token: string): Promise<{
76
+ kind: "cloud";
77
+ projectId: string;
78
+ projectName?: string | null | undefined;
79
+ reference?: string | null | undefined;
80
+ } | {
81
+ kind: "selfHosted";
82
+ }>;
83
+ export interface EmitConvexEventOptions {
84
+ /** Public endpoint exposed by `onConvexEvent`. */
85
+ endpoint: string;
86
+ /** Stable caller-assigned idempotency ID. */
87
+ id: string;
88
+ /** Application-defined semantic event name. */
89
+ name: string;
90
+ /** Convex-compatible payload delivered to the automation. */
91
+ payload: ConvexValue;
92
+ /** Secret configured both on the account and in Convex. */
93
+ signingSecret: string;
94
+ }
95
+ /**
96
+ * Emits one authenticated semantic event from a Convex action.
97
+ *
98
+ * @param options - Endpoint, identity, payload, and signing secret.
99
+ */
100
+ export declare function emitConvexEvent(options: EmitConvexEventOptions): Promise<void>;
101
+ /**
102
+ * Computes the portable HMAC used by Convex semantic-event requests.
103
+ *
104
+ * @param secret - Shared webhook signing secret.
105
+ * @param timestamp - ISO timestamp included in the signature.
106
+ * @param eventId - Caller-assigned idempotency ID.
107
+ * @param body - Exact request body.
108
+ */
109
+ export declare function signConvexEvent(secret: string, timestamp: string, eventId: string, body: string): Promise<string>;
@@ -0,0 +1,214 @@
1
+ import { convexToJson, jsonToConvex } from "convex/values";
2
+ import * as z from "zod";
3
+ import { CONVEX_VALUE_SCHEMA } from "./schemas.js";
4
+ const CONVEX_OAUTH_SECRET_SCHEMA = z.object({
5
+ accessToken: z.string(),
6
+ projectId: z.string(),
7
+ });
8
+ export const CONVEX_DEPLOYMENT_URL_SCHEMA = z.url().refine((value) => {
9
+ if (!URL.canParse(value))
10
+ return false;
11
+ const url = new URL(value);
12
+ return (url.protocol === "https:" &&
13
+ url.hostname.endsWith(".convex.cloud") &&
14
+ url.port === "" &&
15
+ url.username === "" &&
16
+ url.password === "" &&
17
+ url.pathname === "/" &&
18
+ url.search === "" &&
19
+ url.hash === "");
20
+ }, "Enter a Convex Cloud deployment URL such as https://example.convex.cloud.");
21
+ const CONVEX_DEPLOY_KEY_SECRET_SCHEMA = z.object({
22
+ deployKey: z.string(),
23
+ deploymentUrl: CONVEX_DEPLOYMENT_URL_SCHEMA,
24
+ webhookSecret: z.string(),
25
+ });
26
+ export const CONVEX_SECRET_SCHEMA = z.union([
27
+ CONVEX_OAUTH_SECRET_SCHEMA,
28
+ CONVEX_DEPLOY_KEY_SECRET_SCHEMA,
29
+ ]);
30
+ const CONVEX_DEPLOYMENT_SCHEMA = z.object({
31
+ deploymentUrl: CONVEX_DEPLOYMENT_URL_SCHEMA,
32
+ name: z.string(),
33
+ reference: z.string(),
34
+ });
35
+ const CONVEX_DEPLOYMENT_INFO_SCHEMA = z.discriminatedUnion("kind", [
36
+ z.object({
37
+ kind: z.literal("cloud"),
38
+ projectId: z.string(),
39
+ projectName: z.string().nullable().optional(),
40
+ reference: z.string().nullable().optional(),
41
+ }),
42
+ z.object({ kind: z.literal("selfHosted") }),
43
+ ]);
44
+ const CONVEX_FUNCTION_RESPONSE_SCHEMA = z.discriminatedUnion("status", [
45
+ z.object({ status: z.literal("success"), value: z.json() }),
46
+ z.object({ errorMessage: z.string(), status: z.literal("error") }),
47
+ ]);
48
+ /**
49
+ * Resolves the authenticated Convex deployment represented by an account.
50
+ *
51
+ * @param secret - Persisted Convex account secret.
52
+ * @param deployment - Default alias or explicit deployment reference.
53
+ */
54
+ export async function resolveConvexDeployment(secret, deployment = "prod") {
55
+ const parsed = CONVEX_SECRET_SCHEMA.parse(secret);
56
+ if ("deployKey" in parsed) {
57
+ if (deployment !== "prod") {
58
+ throw new Error("A deployment-key account is already bound to one deployment; omit the deployment option.");
59
+ }
60
+ return {
61
+ deploymentUrl: parsed.deploymentUrl,
62
+ reference: "connected",
63
+ token: parsed.deployKey,
64
+ };
65
+ }
66
+ const response = await fetch(`https://api.convex.dev/v1/projects/${encodeURIComponent(parsed.projectId)}/deployment?${new URLSearchParams(deployment === "prod"
67
+ ? { defaultProd: "true" }
68
+ : deployment === "dev"
69
+ ? { defaultDev: "true" }
70
+ : { reference: deployment }).toString()}`, { headers: { Authorization: `Bearer ${parsed.accessToken}` } });
71
+ if (!response.ok) {
72
+ throw new Error(`Convex could not resolve deployment ${deployment} (${response.status}).`);
73
+ }
74
+ const resolved = CONVEX_DEPLOYMENT_SCHEMA.parse(await response.json());
75
+ return {
76
+ deploymentUrl: resolved.deploymentUrl,
77
+ reference: resolved.reference,
78
+ token: parsed.accessToken,
79
+ };
80
+ }
81
+ /**
82
+ * Creates a small authenticated client for named Convex functions.
83
+ *
84
+ * @param secret - Persisted Convex account secret.
85
+ * @param deployment - Default alias or explicit deployment reference.
86
+ */
87
+ export async function getConvexClient(secret, deployment = "prod") {
88
+ const resolved = await resolveConvexDeployment(secret, deployment);
89
+ return {
90
+ action: (functionName, arguments_) => requestConvexFunction("action", resolved, functionName, arguments_),
91
+ mutation: (functionName, arguments_) => requestConvexFunction("mutation", resolved, functionName, arguments_),
92
+ query: (functionName, arguments_) => requestConvexFunction("query", resolved, functionName, arguments_),
93
+ };
94
+ }
95
+ /**
96
+ * Runs a named Convex function through the authenticated deployment client.
97
+ *
98
+ * @param kind - Convex function kind.
99
+ * @param secret - Persisted Convex account secret.
100
+ * @param input - Function path, arguments, and deployment selection.
101
+ * @param input.arguments - Convex arguments keyed by parameter name.
102
+ * @param input.deployment - Default alias or explicit deployment reference.
103
+ * @param input.functionName - Exported Convex function path.
104
+ */
105
+ export async function runConvexFunction(kind, secret, input) {
106
+ return await requestConvexFunction(kind, await resolveConvexDeployment(secret, input.deployment), input.functionName, input.arguments);
107
+ }
108
+ /**
109
+ * Returns the deployment API token stored by either connection method.
110
+ *
111
+ * @param secret - Persisted Convex account secret.
112
+ */
113
+ export function getConvexDeploymentToken(secret) {
114
+ const parsed = CONVEX_SECRET_SCHEMA.parse(secret);
115
+ return "deployKey" in parsed ? parsed.deployKey : parsed.accessToken;
116
+ }
117
+ /**
118
+ * Decodes Convex's tagged JSON representation into a native Convex value.
119
+ *
120
+ * @param value - Tagged JSON returned by a Convex HTTP API.
121
+ */
122
+ export function decodeConvexValue(value) {
123
+ return CONVEX_VALUE_SCHEMA.parse(jsonToConvex(value));
124
+ }
125
+ /**
126
+ * Encodes a native Convex value into Convex's tagged JSON representation.
127
+ *
128
+ * @param value - Native Convex value to encode.
129
+ */
130
+ export function encodeConvexValue(value) {
131
+ return convexToJson(value);
132
+ }
133
+ /**
134
+ * Sends one authenticated Convex function request.
135
+ *
136
+ * @param kind - Convex function kind.
137
+ * @param deployment - Resolved URL and admin token.
138
+ * @param functionName - Exported function path.
139
+ * @param arguments_ - Convex arguments keyed by parameter name.
140
+ */
141
+ async function requestConvexFunction(kind, deployment, functionName, arguments_ = {}) {
142
+ const response = await fetch(new URL(`/api/${kind}`, deployment.deploymentUrl), {
143
+ body: JSON.stringify({
144
+ args: [convexToJson(arguments_)],
145
+ format: "convex_encoded_json",
146
+ path: functionName,
147
+ }),
148
+ headers: {
149
+ Authorization: `Convex ${deployment.token}`,
150
+ "Content-Type": "application/json",
151
+ },
152
+ method: "POST",
153
+ });
154
+ if (!response.ok && response.status !== 560) {
155
+ const details = (await response.text()).trim();
156
+ throw new Error(`Convex function call failed (${response.status})${details ? `: ${details}` : "."}`);
157
+ }
158
+ const result = CONVEX_FUNCTION_RESPONSE_SCHEMA.parse(await response.json());
159
+ if (result.status === "error")
160
+ throw new Error(result.errorMessage);
161
+ return decodeConvexValue(result.value);
162
+ }
163
+ /**
164
+ * Checks a deployment-key connection and returns provider-owned identity.
165
+ *
166
+ * @param deploymentUrl - Connected Convex deployment URL.
167
+ * @param token - Convex deploy key.
168
+ */
169
+ export async function getConvexDeploymentInfo(deploymentUrl, token) {
170
+ const response = await fetch(new URL("/api/v1/deployment_info", CONVEX_DEPLOYMENT_URL_SCHEMA.parse(deploymentUrl)), { headers: { Authorization: `Convex ${token}` } });
171
+ if (!response.ok) {
172
+ throw new Error(`Convex rejected the deployment key (${response.status}).`);
173
+ }
174
+ return CONVEX_DEPLOYMENT_INFO_SCHEMA.parse(await response.json());
175
+ }
176
+ /**
177
+ * Emits one authenticated semantic event from a Convex action.
178
+ *
179
+ * @param options - Endpoint, identity, payload, and signing secret.
180
+ */
181
+ export async function emitConvexEvent(options) {
182
+ const timestamp = new Date().toISOString();
183
+ const body = JSON.stringify({
184
+ emittedAt: timestamp,
185
+ id: options.id,
186
+ name: options.name,
187
+ payload: encodeConvexValue(options.payload),
188
+ });
189
+ const response = await fetch(options.endpoint, {
190
+ body,
191
+ headers: {
192
+ "Content-Type": "application/json",
193
+ "x-automate-ax-event-id": options.id,
194
+ "x-automate-ax-signature": `sha256=${await signConvexEvent(options.signingSecret, timestamp, options.id, body)}`,
195
+ "x-automate-ax-timestamp": timestamp,
196
+ },
197
+ method: "POST",
198
+ });
199
+ if (!response.ok) {
200
+ throw new Error(`Automate.ax rejected the Convex event (${response.status}).`);
201
+ }
202
+ }
203
+ /**
204
+ * Computes the portable HMAC used by Convex semantic-event requests.
205
+ *
206
+ * @param secret - Shared webhook signing secret.
207
+ * @param timestamp - ISO timestamp included in the signature.
208
+ * @param eventId - Caller-assigned idempotency ID.
209
+ * @param body - Exact request body.
210
+ */
211
+ export async function signConvexEvent(secret, timestamp, eventId, body) {
212
+ const encoder = new TextEncoder();
213
+ return Array.from(new Uint8Array(await crypto.subtle.sign("HMAC", await crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign"]), encoder.encode(`${timestamp}.${eventId}.${body}`))), (byte) => byte.toString(16).padStart(2, "0")).join("");
214
+ }
@@ -0,0 +1,112 @@
1
+ import * as z from "zod";
2
+ export * from "./api.js";
3
+ export * from "./schemas.js";
4
+ export declare const CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA: z.ZodObject<{
5
+ component: z.ZodDefault<z.ZodString>;
6
+ deployment: z.ZodDefault<z.ZodString>;
7
+ table: z.ZodString;
8
+ }, z.core.$strip>;
9
+ export declare const CONVEX_EVENT_TRIGGER_CONFIG_SCHEMA: z.ZodObject<{
10
+ name: z.ZodString;
11
+ }, z.core.$strip>;
12
+ export declare const convexTriggerContracts: {
13
+ readonly "convex.document.created": {
14
+ readonly configSchema: z.ZodObject<{
15
+ component: z.ZodDefault<z.ZodString>;
16
+ deployment: z.ZodDefault<z.ZodString>;
17
+ table: z.ZodString;
18
+ }, z.core.$strip>;
19
+ readonly eventSchema: z.ZodObject<{
20
+ action: z.ZodEnum<{
21
+ deleted: "deleted";
22
+ created: "created";
23
+ updated: "updated";
24
+ }>;
25
+ component: z.ZodString;
26
+ document: z.ZodNullable<z.ZodObject<{
27
+ _creationTime: z.ZodOptional<z.ZodNumber>;
28
+ _id: z.ZodString;
29
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<import("./schemas").ConvexValue, unknown, z.core.$ZodTypeInternals<import("./schemas").ConvexValue, unknown>>>>>>;
30
+ documentId: z.ZodString;
31
+ revision: z.ZodBigInt;
32
+ table: z.ZodString;
33
+ }, z.core.$strip>;
34
+ };
35
+ readonly "convex.document.deleted": {
36
+ readonly configSchema: z.ZodObject<{
37
+ component: z.ZodDefault<z.ZodString>;
38
+ deployment: z.ZodDefault<z.ZodString>;
39
+ table: z.ZodString;
40
+ }, z.core.$strip>;
41
+ readonly eventSchema: z.ZodObject<{
42
+ action: z.ZodEnum<{
43
+ deleted: "deleted";
44
+ created: "created";
45
+ updated: "updated";
46
+ }>;
47
+ component: z.ZodString;
48
+ document: z.ZodNullable<z.ZodObject<{
49
+ _creationTime: z.ZodOptional<z.ZodNumber>;
50
+ _id: z.ZodString;
51
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<import("./schemas").ConvexValue, unknown, z.core.$ZodTypeInternals<import("./schemas").ConvexValue, unknown>>>>>>;
52
+ documentId: z.ZodString;
53
+ revision: z.ZodBigInt;
54
+ table: z.ZodString;
55
+ }, z.core.$strip>;
56
+ };
57
+ readonly "convex.document.event": {
58
+ readonly configSchema: z.ZodObject<{
59
+ component: z.ZodDefault<z.ZodString>;
60
+ deployment: z.ZodDefault<z.ZodString>;
61
+ table: z.ZodString;
62
+ }, z.core.$strip>;
63
+ readonly eventSchema: z.ZodObject<{
64
+ action: z.ZodEnum<{
65
+ deleted: "deleted";
66
+ created: "created";
67
+ updated: "updated";
68
+ }>;
69
+ component: z.ZodString;
70
+ document: z.ZodNullable<z.ZodObject<{
71
+ _creationTime: z.ZodOptional<z.ZodNumber>;
72
+ _id: z.ZodString;
73
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<import("./schemas").ConvexValue, unknown, z.core.$ZodTypeInternals<import("./schemas").ConvexValue, unknown>>>>>>;
74
+ documentId: z.ZodString;
75
+ revision: z.ZodBigInt;
76
+ table: z.ZodString;
77
+ }, z.core.$strip>;
78
+ };
79
+ readonly "convex.document.updated": {
80
+ readonly configSchema: z.ZodObject<{
81
+ component: z.ZodDefault<z.ZodString>;
82
+ deployment: z.ZodDefault<z.ZodString>;
83
+ table: z.ZodString;
84
+ }, z.core.$strip>;
85
+ readonly eventSchema: z.ZodObject<{
86
+ action: z.ZodEnum<{
87
+ deleted: "deleted";
88
+ created: "created";
89
+ updated: "updated";
90
+ }>;
91
+ component: z.ZodString;
92
+ document: z.ZodNullable<z.ZodObject<{
93
+ _creationTime: z.ZodOptional<z.ZodNumber>;
94
+ _id: z.ZodString;
95
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<import("./schemas").ConvexValue, unknown, z.core.$ZodTypeInternals<import("./schemas").ConvexValue, unknown>>>>>>;
96
+ documentId: z.ZodString;
97
+ revision: z.ZodBigInt;
98
+ table: z.ZodString;
99
+ }, z.core.$strip>;
100
+ };
101
+ readonly "convex.event.received": {
102
+ readonly configSchema: z.ZodObject<{
103
+ name: z.ZodString;
104
+ }, z.core.$strip>;
105
+ readonly eventSchema: z.ZodObject<{
106
+ id: z.ZodString;
107
+ emittedAt: z.ZodISODateTime;
108
+ name: z.ZodString;
109
+ payload: z.ZodType<import("./schemas").ConvexValue, unknown, z.core.$ZodTypeInternals<import("./schemas").ConvexValue, unknown>>;
110
+ }, z.core.$strip>;
111
+ };
112
+ };
@@ -0,0 +1,34 @@
1
+ import * as z from "zod";
2
+ import { CONVEX_DOCUMENT_EVENT_SCHEMA, CONVEX_SEMANTIC_EVENT_SCHEMA, } from "./schemas.js";
3
+ export * from "./api.js";
4
+ export * from "./schemas.js";
5
+ export const CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA = z.object({
6
+ component: z.string().default(""),
7
+ deployment: z.string().default("prod"),
8
+ table: z.string().min(1),
9
+ });
10
+ export const CONVEX_EVENT_TRIGGER_CONFIG_SCHEMA = z.object({
11
+ name: z.string().min(1),
12
+ });
13
+ export const convexTriggerContracts = {
14
+ "convex.document.created": {
15
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
16
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
17
+ },
18
+ "convex.document.deleted": {
19
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
20
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
21
+ },
22
+ "convex.document.event": {
23
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
24
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
25
+ },
26
+ "convex.document.updated": {
27
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
28
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
29
+ },
30
+ "convex.event.received": {
31
+ configSchema: CONVEX_EVENT_TRIGGER_CONFIG_SCHEMA,
32
+ eventSchema: CONVEX_SEMANTIC_EVENT_SCHEMA,
33
+ },
34
+ };
@@ -0,0 +1,37 @@
1
+ import * as z from "zod";
2
+ /** Recursive schema for values accepted and returned by Convex functions. */
3
+ export declare const CONVEX_VALUE_SCHEMA: z.ZodType<ConvexValue>;
4
+ export type ConvexValue = null | boolean | number | bigint | string | ArrayBuffer | ConvexValue[] | {
5
+ [key: string]: ConvexValue | undefined;
6
+ };
7
+ export declare const CONVEX_DOCUMENT_SCHEMA: z.ZodObject<{
8
+ _creationTime: z.ZodOptional<z.ZodNumber>;
9
+ _id: z.ZodString;
10
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<ConvexValue, unknown, z.core.$ZodTypeInternals<ConvexValue, unknown>>>>>;
11
+ export declare const CONVEX_DOCUMENT_EVENT_TYPES: readonly ["convex.document.event", "convex.document.created", "convex.document.updated", "convex.document.deleted"];
12
+ export declare const CONVEX_DOCUMENT_ACTION_SCHEMA: z.ZodEnum<{
13
+ deleted: "deleted";
14
+ created: "created";
15
+ updated: "updated";
16
+ }>;
17
+ export declare const CONVEX_DOCUMENT_EVENT_SCHEMA: z.ZodObject<{
18
+ action: z.ZodEnum<{
19
+ deleted: "deleted";
20
+ created: "created";
21
+ updated: "updated";
22
+ }>;
23
+ component: z.ZodString;
24
+ document: z.ZodNullable<z.ZodObject<{
25
+ _creationTime: z.ZodOptional<z.ZodNumber>;
26
+ _id: z.ZodString;
27
+ }, z.core.$catchall<z.ZodOptional<z.ZodType<ConvexValue, unknown, z.core.$ZodTypeInternals<ConvexValue, unknown>>>>>>;
28
+ documentId: z.ZodString;
29
+ revision: z.ZodBigInt;
30
+ table: z.ZodString;
31
+ }, z.core.$strip>;
32
+ export declare const CONVEX_SEMANTIC_EVENT_SCHEMA: z.ZodObject<{
33
+ id: z.ZodString;
34
+ emittedAt: z.ZodISODateTime;
35
+ name: z.ZodString;
36
+ payload: z.ZodType<ConvexValue, unknown, z.core.$ZodTypeInternals<ConvexValue, unknown>>;
37
+ }, z.core.$strip>;
@@ -0,0 +1,53 @@
1
+ import * as z from "zod";
2
+ /** Recursive schema for values accepted and returned by Convex functions. */
3
+ export const CONVEX_VALUE_SCHEMA = z.lazy(() => z.union([
4
+ z.null(),
5
+ z.boolean(),
6
+ z.number(),
7
+ z.bigint(),
8
+ z.string(),
9
+ z.instanceof(ArrayBuffer),
10
+ z.array(CONVEX_VALUE_SCHEMA),
11
+ z.record(z.string(), CONVEX_VALUE_SCHEMA.optional()),
12
+ ]));
13
+ export const CONVEX_DOCUMENT_SCHEMA = z
14
+ .object({
15
+ _creationTime: z.number().optional(),
16
+ _id: z.string(),
17
+ })
18
+ .catchall(CONVEX_VALUE_SCHEMA.optional());
19
+ export const CONVEX_DOCUMENT_EVENT_TYPES = [
20
+ "convex.document.event",
21
+ "convex.document.created",
22
+ "convex.document.updated",
23
+ "convex.document.deleted",
24
+ ];
25
+ export const CONVEX_DOCUMENT_ACTION_SCHEMA = z.enum([
26
+ "created",
27
+ "updated",
28
+ "deleted",
29
+ ]);
30
+ export const CONVEX_DOCUMENT_EVENT_SCHEMA = z.object({
31
+ /** Kind of lifecycle change observed for this document. */
32
+ action: CONVEX_DOCUMENT_ACTION_SCHEMA,
33
+ /** Convex component path, or an empty string for the root component. */
34
+ component: z.string(),
35
+ /** Current document, or `null` after deletion. */
36
+ document: CONVEX_DOCUMENT_SCHEMA.nullable(),
37
+ /** Stable Convex document ID. */
38
+ documentId: z.string(),
39
+ /** Monotonic Convex revision timestamp for this change. */
40
+ revision: z.bigint(),
41
+ /** Convex table containing the document. */
42
+ table: z.string(),
43
+ });
44
+ export const CONVEX_SEMANTIC_EVENT_SCHEMA = z.object({
45
+ /** Caller-assigned idempotency ID. */
46
+ id: z.string(),
47
+ /** ISO timestamp supplied by the emitting Convex action. */
48
+ emittedAt: z.iso.datetime({ offset: true }),
49
+ /** Application-defined semantic event name. */
50
+ name: z.string(),
51
+ /** Application-defined event payload. */
52
+ payload: CONVEX_VALUE_SCHEMA,
53
+ });
@@ -1704,10 +1704,7 @@ export declare const LINEAR_PROVIDER_PROJECT_SCHEMA: z.ZodObject<{
1704
1704
  name: z.ZodString;
1705
1705
  color: z.ZodString;
1706
1706
  completedAt: z.ZodNullable<z.ZodUnion<readonly [z.ZodDate, z.ZodPipe<z.ZodISODateTime, z.ZodTransform<Date, string>>]>>;
1707
- priority: z.ZodNumber;
1708
1707
  description: z.ZodString;
1709
- content: z.ZodNullable<z.ZodString>;
1710
- startDate: z.ZodNullable<z.ZodISODate>;
1711
1708
  url: z.ZodString;
1712
1709
  status: z.ZodObject<{
1713
1710
  color: z.ZodString;
@@ -1716,6 +1713,9 @@ export declare const LINEAR_PROVIDER_PROJECT_SCHEMA: z.ZodObject<{
1716
1713
  name: z.ZodString;
1717
1714
  type: z.ZodString;
1718
1715
  }, z.core.$strip>;
1716
+ priority: z.ZodNumber;
1717
+ content: z.ZodNullable<z.ZodString>;
1718
+ startDate: z.ZodNullable<z.ZodISODate>;
1719
1719
  creator: z.ZodNullable<z.ZodObject<{
1720
1720
  active: z.ZodBoolean;
1721
1721
  avatarUrl: z.ZodNullable<z.ZodString>;
@@ -1807,10 +1807,10 @@ export declare const LINEAR_PROVIDER_ISSUE_SCHEMA: z.ZodObject<{
1807
1807
  title: z.ZodString;
1808
1808
  url: z.ZodString;
1809
1809
  }, z.core.$strip>>;
1810
- priority: z.ZodNumber;
1810
+ title: z.ZodString;
1811
1811
  description: z.ZodNullable<z.ZodString>;
1812
1812
  url: z.ZodString;
1813
- title: z.ZodString;
1813
+ priority: z.ZodNumber;
1814
1814
  creator: z.ZodNullable<z.ZodObject<{
1815
1815
  active: z.ZodBoolean;
1816
1816
  avatarUrl: z.ZodNullable<z.ZodString>;
@@ -491,8 +491,8 @@ export declare const OUTLOOK_ATTACHMENT_SCHEMA: z.ZodObject<{
491
491
  type: z.ZodEnum<{
492
492
  unknown: "unknown";
493
493
  file: "file";
494
- item: "item";
495
494
  reference: "reference";
495
+ item: "item";
496
496
  }>;
497
497
  }, z.core.$strip>;
498
498
  export declare const GRAPH_ATTACHMENT_SCHEMA: z.ZodPipe<z.ZodObject<{
@@ -513,7 +513,7 @@ export declare const GRAPH_ATTACHMENT_SCHEMA: z.ZodPipe<z.ZodObject<{
513
513
  isInline: boolean;
514
514
  name: string;
515
515
  size: number;
516
- type: "unknown" | "file" | "item" | "reference";
516
+ type: "unknown" | "file" | "reference" | "item";
517
517
  file?: File | undefined;
518
518
  }, {
519
519
  [x: string]: unknown;
@@ -1,6 +1,7 @@
1
1
  import type { airtableTriggerContracts } from "./airtable/index.js";
2
2
  import type { asanaTriggerContracts } from "./asana/index.js";
3
3
  import type { brevoTriggerContracts } from "./brevo/index.js";
4
+ import type { convexTriggerContracts } from "./convex/index.js";
4
5
  import type { githubTriggerContracts } from "./github/index.js";
5
6
  import type { gmailTriggerContracts } from "./gmail/index.js";
6
7
  import type { googleCalendarTriggerContracts } from "./google-calendar/index.js";
@@ -15,7 +16,7 @@ import type { trelloTriggerContracts } from "./trello/index.js";
15
16
  import type { vercelTriggerContracts } from "./vercel/index.js";
16
17
  import type { whatsappTriggerContracts } from "./whatsapp/index.js";
17
18
  import type { z } from "zod";
18
- export type TriggerContractMap = typeof airtableTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof linearTriggerContracts & typeof outlookTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof whatsappTriggerContracts;
19
+ export type TriggerContractMap = typeof airtableTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof convexTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof linearTriggerContracts & typeof outlookTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof whatsappTriggerContracts;
19
20
  export type IntegrationTriggerType = keyof TriggerContractMap;
20
21
  /** Canonical authoring configuration for one integration trigger type. */
21
22
  export type TriggerConfig<TType extends IntegrationTriggerType> = z.input<TriggerContractMap[TType]["configSchema"]> extends Record<string, never> ? object : z.input<TriggerContractMap[TType]["configSchema"]>;
@@ -324,6 +324,7 @@ export declare const whatsappTriggerContracts: {
324
324
  type: z.ZodEnum<{
325
325
  unknown: "unknown";
326
326
  text: "text";
327
+ document: "document";
327
328
  system: "system";
328
329
  location: "location";
329
330
  image: "image";
@@ -332,7 +333,6 @@ export declare const whatsappTriggerContracts: {
332
333
  audio: "audio";
333
334
  button: "button";
334
335
  contacts: "contacts";
335
- document: "document";
336
336
  interactive: "interactive";
337
337
  order: "order";
338
338
  sticker: "sticker";
@@ -419,6 +419,7 @@ export declare const whatsappTriggerContracts: {
419
419
  type: z.ZodEnum<{
420
420
  unknown: "unknown";
421
421
  text: "text";
422
+ document: "document";
422
423
  system: "system";
423
424
  location: "location";
424
425
  image: "image";
@@ -427,7 +428,6 @@ export declare const whatsappTriggerContracts: {
427
428
  audio: "audio";
428
429
  button: "button";
429
430
  contacts: "contacts";
430
- document: "document";
431
431
  interactive: "interactive";
432
432
  order: "order";
433
433
  sticker: "sticker";
@@ -90,6 +90,7 @@ export declare const WHATSAPP_WEBHOOK_SCHEMA: z.ZodObject<{
90
90
  type: z.ZodEnum<{
91
91
  unknown: "unknown";
92
92
  text: "text";
93
+ document: "document";
93
94
  system: "system";
94
95
  location: "location";
95
96
  image: "image";
@@ -98,7 +99,6 @@ export declare const WHATSAPP_WEBHOOK_SCHEMA: z.ZodObject<{
98
99
  audio: "audio";
99
100
  button: "button";
100
101
  contacts: "contacts";
101
- document: "document";
102
102
  interactive: "interactive";
103
103
  order: "order";
104
104
  sticker: "sticker";
@@ -183,6 +183,7 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
183
183
  type: z.ZodEnum<{
184
184
  unknown: "unknown";
185
185
  text: "text";
186
+ document: "document";
186
187
  system: "system";
187
188
  location: "location";
188
189
  image: "image";
@@ -191,7 +192,6 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
191
192
  audio: "audio";
192
193
  button: "button";
193
194
  contacts: "contacts";
194
- document: "document";
195
195
  interactive: "interactive";
196
196
  order: "order";
197
197
  sticker: "sticker";
@@ -278,6 +278,7 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
278
278
  type: z.ZodEnum<{
279
279
  unknown: "unknown";
280
280
  text: "text";
281
+ document: "document";
281
282
  system: "system";
282
283
  location: "location";
283
284
  image: "image";
@@ -286,7 +287,6 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
286
287
  audio: "audio";
287
288
  button: "button";
288
289
  contacts: "contacts";
289
- document: "document";
290
290
  interactive: "interactive";
291
291
  order: "order";
292
292
  sticker: "sticker";
@@ -768,7 +768,7 @@ export declare function normalizeWhatsAppWebhook(input: unknown): {
768
768
  message: {
769
769
  id: string;
770
770
  timestamp: string;
771
- type: "unknown" | "text" | "system" | "location" | "image" | "video" | "reaction" | "audio" | "button" | "contacts" | "document" | "interactive" | "order" | "sticker" | "unsupported";
771
+ type: "unknown" | "text" | "document" | "system" | "location" | "image" | "video" | "reaction" | "audio" | "button" | "contacts" | "interactive" | "order" | "sticker" | "unsupported";
772
772
  content?: z.core.util.JSONType | undefined;
773
773
  context?: {
774
774
  messageId: string;
@@ -781,7 +781,7 @@ export declare function normalizeWhatsAppWebhook(input: unknown): {
781
781
  from: string;
782
782
  id: string;
783
783
  timestamp: string;
784
- type: "unknown" | "text" | "system" | "location" | "image" | "video" | "reaction" | "audio" | "button" | "contacts" | "document" | "interactive" | "order" | "sticker" | "unsupported";
784
+ type: "unknown" | "text" | "document" | "system" | "location" | "image" | "video" | "reaction" | "audio" | "button" | "contacts" | "interactive" | "order" | "sticker" | "unsupported";
785
785
  audio?: {
786
786
  id: string;
787
787
  mime_type?: string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automate.ax/integration-contracts",
3
- "version": "0.82.0",
3
+ "version": "0.83.0",
4
4
  "description": "Shared integration payload contracts and provider primitives for Automate.ax.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,7 @@
21
21
  "./airtable": "./src/airtable/index.ts",
22
22
  "./asana": "./src/asana/index.ts",
23
23
  "./brevo": "./src/brevo/index.ts",
24
+ "./convex": "./src/convex/index.ts",
24
25
  "./gmail": "./src/gmail/index.ts",
25
26
  "./google-calendar": "./src/google-calendar/index.ts",
26
27
  "./google-forms": "./src/google-forms/index.ts",
@@ -42,13 +43,14 @@
42
43
  }
43
44
  },
44
45
  "dependencies": {
45
- "@automate.ax/codec": "0.82.0",
46
+ "@automate.ax/codec": "0.83.0",
46
47
  "@googleapis/calendar": "^15.0.0",
47
48
  "@googleapis/forms": "^6.0.1",
48
49
  "@googleapis/gmail": "^12.0.0",
49
50
  "@googleapis/meet": "^5.0.0",
50
51
  "@octokit/openapi-webhooks-types": "^12.1.0",
51
52
  "@octokit/rest": "^22.0.1",
53
+ "convex": "^1.45.0",
52
54
  "html-to-text": "^10.0.0",
53
55
  "nodemailer": "^9.0.3",
54
56
  "postal-mime": "^2.7.5",
@@ -56,7 +58,7 @@
56
58
  "zod": "^4.3.6"
57
59
  },
58
60
  "devDependencies": {
59
- "@internal/config": "0.0.0",
61
+ "@internal/config": "0.1.0",
60
62
  "@types/bun": "latest",
61
63
  "@types/html-to-text": "^9.0.4",
62
64
  "@types/nodemailer": "^8.0.1",
@@ -101,6 +103,11 @@
101
103
  "types": "./dist/brevo/index.d.ts",
102
104
  "default": "./dist/brevo/index.js"
103
105
  },
106
+ "./convex": {
107
+ "bun": "./src/convex/index.ts",
108
+ "types": "./dist/convex/index.d.ts",
109
+ "default": "./dist/convex/index.js"
110
+ },
104
111
  "./gmail": {
105
112
  "bun": "./src/gmail/index.ts",
106
113
  "types": "./dist/gmail/index.d.ts",
@@ -0,0 +1,331 @@
1
+ import { convexToJson, jsonToConvex } from "convex/values"
2
+ import type { JSONValue } from "convex/values"
3
+ import * as z from "zod"
4
+ import { CONVEX_VALUE_SCHEMA, type ConvexValue } from "./schemas"
5
+
6
+ const CONVEX_OAUTH_SECRET_SCHEMA = z.object({
7
+ accessToken: z.string(),
8
+ projectId: z.string(),
9
+ })
10
+ export const CONVEX_DEPLOYMENT_URL_SCHEMA = z.url().refine((value) => {
11
+ if (!URL.canParse(value)) return false
12
+ const url = new URL(value)
13
+ return (
14
+ url.protocol === "https:" &&
15
+ url.hostname.endsWith(".convex.cloud") &&
16
+ url.port === "" &&
17
+ url.username === "" &&
18
+ url.password === "" &&
19
+ url.pathname === "/" &&
20
+ url.search === "" &&
21
+ url.hash === ""
22
+ )
23
+ }, "Enter a Convex Cloud deployment URL such as https://example.convex.cloud.")
24
+ const CONVEX_DEPLOY_KEY_SECRET_SCHEMA = z.object({
25
+ deployKey: z.string(),
26
+ deploymentUrl: CONVEX_DEPLOYMENT_URL_SCHEMA,
27
+ webhookSecret: z.string(),
28
+ })
29
+ export const CONVEX_SECRET_SCHEMA = z.union([
30
+ CONVEX_OAUTH_SECRET_SCHEMA,
31
+ CONVEX_DEPLOY_KEY_SECRET_SCHEMA,
32
+ ])
33
+
34
+ const CONVEX_DEPLOYMENT_SCHEMA = z.object({
35
+ deploymentUrl: CONVEX_DEPLOYMENT_URL_SCHEMA,
36
+ name: z.string(),
37
+ reference: z.string(),
38
+ })
39
+ const CONVEX_DEPLOYMENT_INFO_SCHEMA = z.discriminatedUnion("kind", [
40
+ z.object({
41
+ kind: z.literal("cloud"),
42
+ projectId: z.string(),
43
+ projectName: z.string().nullable().optional(),
44
+ reference: z.string().nullable().optional(),
45
+ }),
46
+ z.object({ kind: z.literal("selfHosted") }),
47
+ ])
48
+ const CONVEX_FUNCTION_RESPONSE_SCHEMA = z.discriminatedUnion("status", [
49
+ z.object({ status: z.literal("success"), value: z.json() }),
50
+ z.object({ errorMessage: z.string(), status: z.literal("error") }),
51
+ ])
52
+
53
+ export interface ConvexDeployment {
54
+ deploymentUrl: string
55
+ reference: string
56
+ token: string
57
+ }
58
+
59
+ /**
60
+ * Resolves the authenticated Convex deployment represented by an account.
61
+ *
62
+ * @param secret - Persisted Convex account secret.
63
+ * @param deployment - Default alias or explicit deployment reference.
64
+ */
65
+ export async function resolveConvexDeployment(
66
+ secret: unknown,
67
+ deployment = "prod",
68
+ ): Promise<ConvexDeployment> {
69
+ const parsed = CONVEX_SECRET_SCHEMA.parse(secret)
70
+ if ("deployKey" in parsed) {
71
+ if (deployment !== "prod") {
72
+ throw new Error(
73
+ "A deployment-key account is already bound to one deployment; omit the deployment option.",
74
+ )
75
+ }
76
+ return {
77
+ deploymentUrl: parsed.deploymentUrl,
78
+ reference: "connected",
79
+ token: parsed.deployKey,
80
+ }
81
+ }
82
+
83
+ const response = await fetch(
84
+ `https://api.convex.dev/v1/projects/${encodeURIComponent(parsed.projectId)}/deployment?${new URLSearchParams(
85
+ deployment === "prod"
86
+ ? { defaultProd: "true" }
87
+ : deployment === "dev"
88
+ ? { defaultDev: "true" }
89
+ : { reference: deployment },
90
+ ).toString()}`,
91
+ { headers: { Authorization: `Bearer ${parsed.accessToken}` } },
92
+ )
93
+ if (!response.ok) {
94
+ throw new Error(
95
+ `Convex could not resolve deployment ${deployment} (${response.status}).`,
96
+ )
97
+ }
98
+ const resolved = CONVEX_DEPLOYMENT_SCHEMA.parse(await response.json())
99
+ return {
100
+ deploymentUrl: resolved.deploymentUrl,
101
+ reference: resolved.reference,
102
+ token: parsed.accessToken,
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Creates a small authenticated client for named Convex functions.
108
+ *
109
+ * @param secret - Persisted Convex account secret.
110
+ * @param deployment - Default alias or explicit deployment reference.
111
+ */
112
+ export async function getConvexClient(secret: unknown, deployment = "prod") {
113
+ const resolved = await resolveConvexDeployment(secret, deployment)
114
+ return {
115
+ action: (
116
+ functionName: string,
117
+ arguments_?: Record<string, ConvexValue | undefined>,
118
+ ) => requestConvexFunction("action", resolved, functionName, arguments_),
119
+ mutation: (
120
+ functionName: string,
121
+ arguments_?: Record<string, ConvexValue | undefined>,
122
+ ) => requestConvexFunction("mutation", resolved, functionName, arguments_),
123
+ query: (
124
+ functionName: string,
125
+ arguments_?: Record<string, ConvexValue | undefined>,
126
+ ) => requestConvexFunction("query", resolved, functionName, arguments_),
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Runs a named Convex function through the authenticated deployment client.
132
+ *
133
+ * @param kind - Convex function kind.
134
+ * @param secret - Persisted Convex account secret.
135
+ * @param input - Function path, arguments, and deployment selection.
136
+ * @param input.arguments - Convex arguments keyed by parameter name.
137
+ * @param input.deployment - Default alias or explicit deployment reference.
138
+ * @param input.functionName - Exported Convex function path.
139
+ */
140
+ export async function runConvexFunction(
141
+ kind: "action" | "mutation" | "query",
142
+ secret: unknown,
143
+ input: {
144
+ arguments?: Record<string, ConvexValue | undefined>
145
+ deployment?: string
146
+ functionName: string
147
+ },
148
+ ) {
149
+ return await requestConvexFunction(
150
+ kind,
151
+ await resolveConvexDeployment(secret, input.deployment),
152
+ input.functionName,
153
+ input.arguments,
154
+ )
155
+ }
156
+
157
+ /**
158
+ * Returns the deployment API token stored by either connection method.
159
+ *
160
+ * @param secret - Persisted Convex account secret.
161
+ */
162
+ export function getConvexDeploymentToken(secret: unknown) {
163
+ const parsed = CONVEX_SECRET_SCHEMA.parse(secret)
164
+ return "deployKey" in parsed ? parsed.deployKey : parsed.accessToken
165
+ }
166
+
167
+ /**
168
+ * Decodes Convex's tagged JSON representation into a native Convex value.
169
+ *
170
+ * @param value - Tagged JSON returned by a Convex HTTP API.
171
+ */
172
+ export function decodeConvexValue(value: JSONValue) {
173
+ return CONVEX_VALUE_SCHEMA.parse(jsonToConvex(value))
174
+ }
175
+
176
+ /**
177
+ * Encodes a native Convex value into Convex's tagged JSON representation.
178
+ *
179
+ * @param value - Native Convex value to encode.
180
+ */
181
+ export function encodeConvexValue(value: ConvexValue) {
182
+ return convexToJson(value)
183
+ }
184
+
185
+ /**
186
+ * Sends one authenticated Convex function request.
187
+ *
188
+ * @param kind - Convex function kind.
189
+ * @param deployment - Resolved URL and admin token.
190
+ * @param functionName - Exported function path.
191
+ * @param arguments_ - Convex arguments keyed by parameter name.
192
+ */
193
+ async function requestConvexFunction(
194
+ kind: "action" | "mutation" | "query",
195
+ deployment: ConvexDeployment,
196
+ functionName: string,
197
+ arguments_: Record<string, ConvexValue | undefined> = {},
198
+ ) {
199
+ const response = await fetch(
200
+ new URL(`/api/${kind}`, deployment.deploymentUrl),
201
+ {
202
+ body: JSON.stringify({
203
+ args: [convexToJson(arguments_)],
204
+ format: "convex_encoded_json",
205
+ path: functionName,
206
+ }),
207
+ headers: {
208
+ Authorization: `Convex ${deployment.token}`,
209
+ "Content-Type": "application/json",
210
+ },
211
+ method: "POST",
212
+ },
213
+ )
214
+ if (!response.ok && response.status !== 560) {
215
+ const details = (await response.text()).trim()
216
+ throw new Error(
217
+ `Convex function call failed (${response.status})${details ? `: ${details}` : "."}`,
218
+ )
219
+ }
220
+ const result = CONVEX_FUNCTION_RESPONSE_SCHEMA.parse(await response.json())
221
+ if (result.status === "error") throw new Error(result.errorMessage)
222
+ return decodeConvexValue(result.value)
223
+ }
224
+
225
+ /**
226
+ * Checks a deployment-key connection and returns provider-owned identity.
227
+ *
228
+ * @param deploymentUrl - Connected Convex deployment URL.
229
+ * @param token - Convex deploy key.
230
+ */
231
+ export async function getConvexDeploymentInfo(
232
+ deploymentUrl: string,
233
+ token: string,
234
+ ) {
235
+ const response = await fetch(
236
+ new URL(
237
+ "/api/v1/deployment_info",
238
+ CONVEX_DEPLOYMENT_URL_SCHEMA.parse(deploymentUrl),
239
+ ),
240
+ { headers: { Authorization: `Convex ${token}` } },
241
+ )
242
+ if (!response.ok) {
243
+ throw new Error(`Convex rejected the deployment key (${response.status}).`)
244
+ }
245
+ return CONVEX_DEPLOYMENT_INFO_SCHEMA.parse(await response.json())
246
+ }
247
+
248
+ export interface EmitConvexEventOptions {
249
+ /** Public endpoint exposed by `onConvexEvent`. */
250
+ endpoint: string
251
+
252
+ /** Stable caller-assigned idempotency ID. */
253
+ id: string
254
+
255
+ /** Application-defined semantic event name. */
256
+ name: string
257
+
258
+ /** Convex-compatible payload delivered to the automation. */
259
+ payload: ConvexValue
260
+
261
+ /** Secret configured both on the account and in Convex. */
262
+ signingSecret: string
263
+ }
264
+
265
+ /**
266
+ * Emits one authenticated semantic event from a Convex action.
267
+ *
268
+ * @param options - Endpoint, identity, payload, and signing secret.
269
+ */
270
+ export async function emitConvexEvent(options: EmitConvexEventOptions) {
271
+ const timestamp = new Date().toISOString()
272
+ const body = JSON.stringify({
273
+ emittedAt: timestamp,
274
+ id: options.id,
275
+ name: options.name,
276
+ payload: encodeConvexValue(options.payload),
277
+ })
278
+ const response = await fetch(options.endpoint, {
279
+ body,
280
+ headers: {
281
+ "Content-Type": "application/json",
282
+ "x-automate-ax-event-id": options.id,
283
+ "x-automate-ax-signature": `sha256=${await signConvexEvent(
284
+ options.signingSecret,
285
+ timestamp,
286
+ options.id,
287
+ body,
288
+ )}`,
289
+ "x-automate-ax-timestamp": timestamp,
290
+ },
291
+ method: "POST",
292
+ })
293
+ if (!response.ok) {
294
+ throw new Error(
295
+ `Automate.ax rejected the Convex event (${response.status}).`,
296
+ )
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Computes the portable HMAC used by Convex semantic-event requests.
302
+ *
303
+ * @param secret - Shared webhook signing secret.
304
+ * @param timestamp - ISO timestamp included in the signature.
305
+ * @param eventId - Caller-assigned idempotency ID.
306
+ * @param body - Exact request body.
307
+ */
308
+ export async function signConvexEvent(
309
+ secret: string,
310
+ timestamp: string,
311
+ eventId: string,
312
+ body: string,
313
+ ) {
314
+ const encoder = new TextEncoder()
315
+ return Array.from(
316
+ new Uint8Array(
317
+ await crypto.subtle.sign(
318
+ "HMAC",
319
+ await crypto.subtle.importKey(
320
+ "raw",
321
+ encoder.encode(secret),
322
+ { hash: "SHA-256", name: "HMAC" },
323
+ false,
324
+ ["sign"],
325
+ ),
326
+ encoder.encode(`${timestamp}.${eventId}.${body}`),
327
+ ),
328
+ ),
329
+ (byte) => byte.toString(16).padStart(2, "0"),
330
+ ).join("")
331
+ }
@@ -0,0 +1,41 @@
1
+ import * as z from "zod"
2
+ import {
3
+ CONVEX_DOCUMENT_EVENT_SCHEMA,
4
+ CONVEX_SEMANTIC_EVENT_SCHEMA,
5
+ } from "./schemas"
6
+
7
+ export * from "./api"
8
+ export * from "./schemas"
9
+
10
+ export const CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA = z.object({
11
+ component: z.string().default(""),
12
+ deployment: z.string().default("prod"),
13
+ table: z.string().min(1),
14
+ })
15
+
16
+ export const CONVEX_EVENT_TRIGGER_CONFIG_SCHEMA = z.object({
17
+ name: z.string().min(1),
18
+ })
19
+
20
+ export const convexTriggerContracts = {
21
+ "convex.document.created": {
22
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
23
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
24
+ },
25
+ "convex.document.deleted": {
26
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
27
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
28
+ },
29
+ "convex.document.event": {
30
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
31
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
32
+ },
33
+ "convex.document.updated": {
34
+ configSchema: CONVEX_DOCUMENT_TRIGGER_CONFIG_SCHEMA,
35
+ eventSchema: CONVEX_DOCUMENT_EVENT_SCHEMA,
36
+ },
37
+ "convex.event.received": {
38
+ configSchema: CONVEX_EVENT_TRIGGER_CONFIG_SCHEMA,
39
+ eventSchema: CONVEX_SEMANTIC_EVENT_SCHEMA,
40
+ },
41
+ } as const
@@ -0,0 +1,79 @@
1
+ import * as z from "zod"
2
+
3
+ /** Recursive schema for values accepted and returned by Convex functions. */
4
+ export const CONVEX_VALUE_SCHEMA: z.ZodType<ConvexValue> = z.lazy(() =>
5
+ z.union([
6
+ z.null(),
7
+ z.boolean(),
8
+ z.number(),
9
+ z.bigint(),
10
+ z.string(),
11
+ z.instanceof(ArrayBuffer),
12
+ z.array(CONVEX_VALUE_SCHEMA),
13
+ z.record(z.string(), CONVEX_VALUE_SCHEMA.optional()),
14
+ ]),
15
+ )
16
+
17
+ export type ConvexValue =
18
+ | null
19
+ | boolean
20
+ | number
21
+ | bigint
22
+ | string
23
+ | ArrayBuffer
24
+ | ConvexValue[]
25
+ | { [key: string]: ConvexValue | undefined }
26
+
27
+ export const CONVEX_DOCUMENT_SCHEMA = z
28
+ .object({
29
+ _creationTime: z.number().optional(),
30
+ _id: z.string(),
31
+ })
32
+ .catchall(CONVEX_VALUE_SCHEMA.optional())
33
+
34
+ export const CONVEX_DOCUMENT_EVENT_TYPES = [
35
+ "convex.document.event",
36
+ "convex.document.created",
37
+ "convex.document.updated",
38
+ "convex.document.deleted",
39
+ ] as const
40
+
41
+ export const CONVEX_DOCUMENT_ACTION_SCHEMA = z.enum([
42
+ "created",
43
+ "updated",
44
+ "deleted",
45
+ ])
46
+
47
+ export const CONVEX_DOCUMENT_EVENT_SCHEMA = z.object({
48
+ /** Kind of lifecycle change observed for this document. */
49
+ action: CONVEX_DOCUMENT_ACTION_SCHEMA,
50
+
51
+ /** Convex component path, or an empty string for the root component. */
52
+ component: z.string(),
53
+
54
+ /** Current document, or `null` after deletion. */
55
+ document: CONVEX_DOCUMENT_SCHEMA.nullable(),
56
+
57
+ /** Stable Convex document ID. */
58
+ documentId: z.string(),
59
+
60
+ /** Monotonic Convex revision timestamp for this change. */
61
+ revision: z.bigint(),
62
+
63
+ /** Convex table containing the document. */
64
+ table: z.string(),
65
+ })
66
+
67
+ export const CONVEX_SEMANTIC_EVENT_SCHEMA = z.object({
68
+ /** Caller-assigned idempotency ID. */
69
+ id: z.string(),
70
+
71
+ /** ISO timestamp supplied by the emitting Convex action. */
72
+ emittedAt: z.iso.datetime({ offset: true }),
73
+
74
+ /** Application-defined semantic event name. */
75
+ name: z.string(),
76
+
77
+ /** Application-defined event payload. */
78
+ payload: CONVEX_VALUE_SCHEMA,
79
+ })
package/src/triggers.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { airtableTriggerContracts } from "./airtable"
2
2
  import type { asanaTriggerContracts } from "./asana"
3
3
  import type { brevoTriggerContracts } from "./brevo"
4
+ import type { convexTriggerContracts } from "./convex"
4
5
  import type { githubTriggerContracts } from "./github"
5
6
  import type { gmailTriggerContracts } from "./gmail"
6
7
  import type { googleCalendarTriggerContracts } from "./google-calendar"
@@ -19,6 +20,7 @@ import type { z } from "zod"
19
20
  export type TriggerContractMap = typeof airtableTriggerContracts &
20
21
  typeof asanaTriggerContracts &
21
22
  typeof brevoTriggerContracts &
23
+ typeof convexTriggerContracts &
22
24
  typeof githubTriggerContracts &
23
25
  typeof gmailTriggerContracts &
24
26
  typeof googleCalendarTriggerContracts &