zitejs 0.9.126 → 0.9.128
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.
- package/dist/cjs/backend/index.d.ts +66 -3
- package/dist/cjs/backend/index.test.js +53 -1
- package/dist/cjs/sync/lib.js +8 -3
- package/dist/cjs/sync/lib.test.js +8 -0
- package/dist/esm/backend/index.d.ts +66 -3
- package/dist/esm/backend/index.test.js +54 -2
- package/dist/esm/sync/lib.js +8 -3
- package/dist/esm/sync/lib.test.js +8 -0
- package/package.json +1 -1
|
@@ -21,14 +21,71 @@ export type ZiteTrigger = {
|
|
|
21
21
|
/** The sentence the editor shows for what fires this endpoint, e.g. "When a task is completed in Asana". */
|
|
22
22
|
label?: string;
|
|
23
23
|
paused?: boolean;
|
|
24
|
+
every?: never;
|
|
25
|
+
events?: never;
|
|
24
26
|
} | {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The connection's name in `zite.config.json`, for a provider whose
|
|
29
|
+
* webhooks come through Zite's own app (Slack, HubSpot, Notion,
|
|
30
|
+
* Intercom, QuickBooks). Nothing is registered: the platform routes
|
|
31
|
+
* that account's events of these types to the endpoint.
|
|
32
|
+
*/
|
|
33
|
+
integration: string;
|
|
34
|
+
/** Event names as the platform lists them for the provider, e.g. `message.channels`, `contact.creation`. */
|
|
35
|
+
events: string[];
|
|
36
|
+
/** The sentence the editor shows for what fires this endpoint, e.g. "When a message is posted in Slack". */
|
|
37
|
+
label?: string;
|
|
38
|
+
paused?: boolean;
|
|
39
|
+
every?: never;
|
|
40
|
+
} | {
|
|
41
|
+
/** `<n>m` (1–59) or `<n>h` (1–23). What makes the trigger a poll. */
|
|
27
42
|
every: string;
|
|
43
|
+
/** The connection the poll checks, if any, so the editor can name it. */
|
|
44
|
+
integration?: string;
|
|
28
45
|
/** The sentence the editor shows for this poll, e.g. "Every 10 minutes, new Stripe charges". */
|
|
29
46
|
label?: string;
|
|
30
47
|
paused?: boolean;
|
|
48
|
+
events?: never;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* What a provider trigger's `subscribe` export receives. Write it as
|
|
52
|
+
* `export const subscribe = (async input => { ... }) satisfies ZiteTriggerSubscribe`
|
|
53
|
+
* so the input is typed and a wrong return shape is a typecheck error.
|
|
54
|
+
*/
|
|
55
|
+
export type ZiteTriggerSubscribeInput = {
|
|
56
|
+
/** Where the provider must deliver, token included. */
|
|
57
|
+
notificationUrl: string;
|
|
58
|
+
/** The connection's name, as the trigger names it. */
|
|
59
|
+
integration: string;
|
|
60
|
+
endpointId: string;
|
|
61
|
+
/** A fresh 64-char hex secret, for providers that let the caller choose the signing secret. */
|
|
62
|
+
secret: string;
|
|
63
|
+
/** Set on a re-subscribe, including a renewal. */
|
|
64
|
+
previous: {
|
|
65
|
+
externalId: string;
|
|
66
|
+
state: Record<string, unknown>;
|
|
67
|
+
} | null;
|
|
68
|
+
};
|
|
69
|
+
export type ZiteTriggerSubscribeResult = {
|
|
70
|
+
/** The provider's id for the registration; what `unsubscribe` receives. */
|
|
71
|
+
externalId: string;
|
|
72
|
+
/** The signing secret, if the provider issued one or accepted `input.secret`. */
|
|
73
|
+
secret?: string;
|
|
74
|
+
/** Anything `unsubscribe` needs later; handed back to it and to the next `subscribe`. */
|
|
75
|
+
state?: Record<string, unknown>;
|
|
76
|
+
/** The provider's starting change marker, for notifications that only say something changed. */
|
|
77
|
+
cursor?: string;
|
|
78
|
+
/** ISO time the provider says the registration lapses, read from its response. */
|
|
79
|
+
expiresAt?: string;
|
|
31
80
|
};
|
|
81
|
+
export type ZiteTriggerUnsubscribeInput = {
|
|
82
|
+
externalId: string;
|
|
83
|
+
state: Record<string, unknown>;
|
|
84
|
+
integration: string;
|
|
85
|
+
endpointId: string;
|
|
86
|
+
};
|
|
87
|
+
export type ZiteTriggerSubscribe = (input: ZiteTriggerSubscribeInput) => Promise<ZiteTriggerSubscribeResult> | ZiteTriggerSubscribeResult;
|
|
88
|
+
export type ZiteTriggerUnsubscribe = (input: ZiteTriggerUnsubscribeInput) => Promise<void> | void;
|
|
32
89
|
/**
|
|
33
90
|
* What a poll-triggered run receives as `input.__poll`. Only on a fire from
|
|
34
91
|
* the poll's schedule; a request call to the same endpoint carries none.
|
|
@@ -56,6 +113,8 @@ export type ZiteTriggerDelivery = {
|
|
|
56
113
|
/** Ids earlier deliveries returned as `handled`, newest last. Skip these. */
|
|
57
114
|
seen: string[];
|
|
58
115
|
};
|
|
116
|
+
/** What an app event trigger's run carries as `input.__webhook`: one event, no cursor. */
|
|
117
|
+
export type ZiteAppEventDelivery = Omit<ZiteTriggerDelivery, "cursor" | "seen">;
|
|
59
118
|
/**
|
|
60
119
|
* What a poll-triggered run, or a provider trigger's delivery, returns for
|
|
61
120
|
* the platform to keep: its new `cursor`, and the id of every item it acted
|
|
@@ -159,9 +218,13 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
|
|
|
159
218
|
validatesInput?: boolean;
|
|
160
219
|
execute: (params: {
|
|
161
220
|
input: TTrigger extends {
|
|
162
|
-
|
|
221
|
+
every: string;
|
|
163
222
|
} ? TInput & {
|
|
164
223
|
__poll?: ZitePollInput;
|
|
224
|
+
} : TTrigger extends {
|
|
225
|
+
events: string[];
|
|
226
|
+
} ? TInput & {
|
|
227
|
+
__webhook?: ZiteAppEventDelivery;
|
|
165
228
|
} : TTrigger extends {
|
|
166
229
|
integration: string;
|
|
167
230
|
} ? TInput & {
|
|
@@ -136,7 +136,7 @@ const requestContext = { user: { id: "u1", email: "a@b.c" } };
|
|
|
136
136
|
(0, vitest_1.it)("hands a poll its cursor and seen ids, typed", async () => {
|
|
137
137
|
const endpoint = (0, index_js_1.createEndpoint)({
|
|
138
138
|
inputSchema: zod_1.z.object({}),
|
|
139
|
-
trigger: {
|
|
139
|
+
trigger: { every: "5m", integration: "stripe" },
|
|
140
140
|
execute: async ({ input }) => {
|
|
141
141
|
const seen = new Set(input.__poll?.seen);
|
|
142
142
|
const items = ["a", "b"].filter((id) => !seen.has(id));
|
|
@@ -158,6 +158,32 @@ const requestContext = { user: { id: "u1", email: "a@b.c" } };
|
|
|
158
158
|
},
|
|
159
159
|
});
|
|
160
160
|
});
|
|
161
|
+
(0, vitest_1.it)("types a trigger's registration exports and checks what subscribe returns", async () => {
|
|
162
|
+
const subscribe = (async (input) => ({
|
|
163
|
+
externalId: `hook-for-${input.endpointId}`,
|
|
164
|
+
secret: input.secret,
|
|
165
|
+
state: { repo: "acme/api" },
|
|
166
|
+
}));
|
|
167
|
+
const unsubscribe = (async (input) => {
|
|
168
|
+
(0, vitest_1.expect)(input.state.repo).toBe("acme/api");
|
|
169
|
+
});
|
|
170
|
+
// @ts-expect-error subscribe has to return the provider's id
|
|
171
|
+
void (async () => ({ secret: "s" }));
|
|
172
|
+
const registered = await subscribe({
|
|
173
|
+
notificationUrl: "https://x",
|
|
174
|
+
integration: "github",
|
|
175
|
+
endpointId: "onPush",
|
|
176
|
+
secret: "abc",
|
|
177
|
+
previous: null,
|
|
178
|
+
});
|
|
179
|
+
(0, vitest_1.expect)(registered.externalId).toBe("hook-for-onPush");
|
|
180
|
+
await unsubscribe({
|
|
181
|
+
externalId: registered.externalId,
|
|
182
|
+
state: registered.state ?? {},
|
|
183
|
+
integration: "github",
|
|
184
|
+
endpointId: "onPush",
|
|
185
|
+
});
|
|
186
|
+
});
|
|
161
187
|
(0, vitest_1.it)("hands a provider trigger's delivery its cursor and seen ids, typed", async () => {
|
|
162
188
|
const endpoint = (0, index_js_1.createEndpoint)({
|
|
163
189
|
inputSchema: zod_1.z.object({}),
|
|
@@ -175,6 +201,32 @@ const requestContext = { user: { id: "u1", email: "a@b.c" } };
|
|
|
175
201
|
context: { user: null },
|
|
176
202
|
})).resolves.toEqual({ cursor: "sync1+1", handled: ["e2"] });
|
|
177
203
|
});
|
|
204
|
+
(0, vitest_1.it)("types an app event trigger's run as one event, with no cursor to keep", async () => {
|
|
205
|
+
const endpoint = (0, index_js_1.createEndpoint)({
|
|
206
|
+
inputSchema: zod_1.z.object({ event: zod_1.z.object({ text: zod_1.z.string() }) }),
|
|
207
|
+
trigger: { integration: "team-slack", events: ["message.channels"] },
|
|
208
|
+
execute: async ({ input }) => {
|
|
209
|
+
// @ts-expect-error an app event delivery has no cursor
|
|
210
|
+
void input.__webhook?.cursor;
|
|
211
|
+
return { id: input.__webhook?.deliveryId ?? null };
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
await (0, vitest_1.expect)(endpoint.execute({
|
|
215
|
+
input: {
|
|
216
|
+
event: { text: "hi" },
|
|
217
|
+
__webhook: { headers: {}, deliveryId: "slack:Ev1" },
|
|
218
|
+
},
|
|
219
|
+
context: { user: null },
|
|
220
|
+
})).resolves.toEqual({ id: "slack:Ev1" });
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.it)("refuses events on a trigger that also polls", () => {
|
|
223
|
+
(0, index_js_1.createEndpoint)({
|
|
224
|
+
inputSchema: zod_1.z.object({}),
|
|
225
|
+
// @ts-expect-error a poll can't also take events
|
|
226
|
+
trigger: { every: "5m", events: ["message.channels"] },
|
|
227
|
+
execute: async () => ({}),
|
|
228
|
+
});
|
|
229
|
+
});
|
|
178
230
|
(0, vitest_1.it)("does not validate a webhook fire", async () => {
|
|
179
231
|
const endpoint = (0, index_js_1.createEndpoint)({
|
|
180
232
|
inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
|
package/dist/cjs/sync/lib.js
CHANGED
|
@@ -1232,10 +1232,13 @@ function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1232
1232
|
"// Auto-generated type-narrowing wrapper. Do not edit manually.",
|
|
1233
1233
|
"// Re-exports createEndpoint with context.user typed to the app User.",
|
|
1234
1234
|
"",
|
|
1235
|
-
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteTrigger, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1235
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteTrigger, ZiteTriggerDelivery, ZiteAppEventDelivery, ZitePollInput, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1236
1236
|
"import type { User } from 'zitejs/auth';",
|
|
1237
1237
|
"",
|
|
1238
1238
|
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1239
|
+
// A trigger's registration exports are typed against these, and they
|
|
1240
|
+
// only reach app code through this file.
|
|
1241
|
+
"export type { ZiteAppEventDelivery, ZitePollInput, ZitePollResult, ZiteTriggerDelivery, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1239
1242
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1240
1243
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1241
1244
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1322,11 +1325,13 @@ function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1322
1325
|
" schedule?: TSchedule;",
|
|
1323
1326
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1324
1327
|
" webhook?: TWebhook;",
|
|
1325
|
-
" /** When set, a
|
|
1328
|
+
" /** When set, a connection's events (registered by this file's own `subscribe` export) or a poll (`every`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1326
1329
|
" trigger?: TTrigger;",
|
|
1327
1330
|
" execute: (",
|
|
1328
1331
|
" params: {",
|
|
1329
|
-
|
|
1332
|
+
// Mirrors the base module: a poll's run carries `__poll`, a provider
|
|
1333
|
+
// trigger's delivery `__webhook`.
|
|
1334
|
+
" input: TTrigger extends { every: string } ? TInput & { __poll?: ZitePollInput } : TTrigger extends { events: string[] } ? TInput & { __webhook?: ZiteAppEventDelivery } : TTrigger extends { integration: string } ? TInput & { __webhook?: ZiteTriggerDelivery } : TInput;",
|
|
1330
1335
|
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
1331
1336
|
" } & (TStream extends true ? { stream: ZiteStreamInterface } : {}),",
|
|
1332
1337
|
" ) => Promise<TOutput> | TOutput;",
|
|
@@ -387,6 +387,14 @@ const duplicateIdentifierErrorsIn = (source) => {
|
|
|
387
387
|
(0, vitest_1.expect)(output).toContain('TTrigger extends ZiteTrigger | undefined = undefined');
|
|
388
388
|
(0, vitest_1.expect)(output).toContain('ZiteTrigger, ZiteWebhook }');
|
|
389
389
|
});
|
|
390
|
+
(0, vitest_1.it)('passes the trigger registration types and trigger inputs through to app code', () => {
|
|
391
|
+
(0, vitest_1.expect)(output).toContain('ZiteTriggerSubscribe, ZiteTriggerSubscribeInput');
|
|
392
|
+
(0, vitest_1.expect)(output).toContain('ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput');
|
|
393
|
+
(0, vitest_1.expect)(output).toContain("TTrigger extends { every: string } ? TInput & { __poll?: ZitePollInput }");
|
|
394
|
+
(0, vitest_1.expect)(output).toContain('TInput & { __webhook?: ZiteTriggerDelivery }');
|
|
395
|
+
(0, vitest_1.expect)(output).toContain("TTrigger extends { events: string[] } ? TInput & { __webhook?: ZiteAppEventDelivery }");
|
|
396
|
+
(0, vitest_1.expect)(output).not.toContain('src/triggers/');
|
|
397
|
+
});
|
|
390
398
|
(0, vitest_1.it)('exports ZiteError', () => {
|
|
391
399
|
(0, vitest_1.expect)(output).toContain('ZiteError');
|
|
392
400
|
});
|
|
@@ -21,14 +21,71 @@ export type ZiteTrigger = {
|
|
|
21
21
|
/** The sentence the editor shows for what fires this endpoint, e.g. "When a task is completed in Asana". */
|
|
22
22
|
label?: string;
|
|
23
23
|
paused?: boolean;
|
|
24
|
+
every?: never;
|
|
25
|
+
events?: never;
|
|
24
26
|
} | {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The connection's name in `zite.config.json`, for a provider whose
|
|
29
|
+
* webhooks come through Zite's own app (Slack, HubSpot, Notion,
|
|
30
|
+
* Intercom, QuickBooks). Nothing is registered: the platform routes
|
|
31
|
+
* that account's events of these types to the endpoint.
|
|
32
|
+
*/
|
|
33
|
+
integration: string;
|
|
34
|
+
/** Event names as the platform lists them for the provider, e.g. `message.channels`, `contact.creation`. */
|
|
35
|
+
events: string[];
|
|
36
|
+
/** The sentence the editor shows for what fires this endpoint, e.g. "When a message is posted in Slack". */
|
|
37
|
+
label?: string;
|
|
38
|
+
paused?: boolean;
|
|
39
|
+
every?: never;
|
|
40
|
+
} | {
|
|
41
|
+
/** `<n>m` (1–59) or `<n>h` (1–23). What makes the trigger a poll. */
|
|
27
42
|
every: string;
|
|
43
|
+
/** The connection the poll checks, if any, so the editor can name it. */
|
|
44
|
+
integration?: string;
|
|
28
45
|
/** The sentence the editor shows for this poll, e.g. "Every 10 minutes, new Stripe charges". */
|
|
29
46
|
label?: string;
|
|
30
47
|
paused?: boolean;
|
|
48
|
+
events?: never;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* What a provider trigger's `subscribe` export receives. Write it as
|
|
52
|
+
* `export const subscribe = (async input => { ... }) satisfies ZiteTriggerSubscribe`
|
|
53
|
+
* so the input is typed and a wrong return shape is a typecheck error.
|
|
54
|
+
*/
|
|
55
|
+
export type ZiteTriggerSubscribeInput = {
|
|
56
|
+
/** Where the provider must deliver, token included. */
|
|
57
|
+
notificationUrl: string;
|
|
58
|
+
/** The connection's name, as the trigger names it. */
|
|
59
|
+
integration: string;
|
|
60
|
+
endpointId: string;
|
|
61
|
+
/** A fresh 64-char hex secret, for providers that let the caller choose the signing secret. */
|
|
62
|
+
secret: string;
|
|
63
|
+
/** Set on a re-subscribe, including a renewal. */
|
|
64
|
+
previous: {
|
|
65
|
+
externalId: string;
|
|
66
|
+
state: Record<string, unknown>;
|
|
67
|
+
} | null;
|
|
68
|
+
};
|
|
69
|
+
export type ZiteTriggerSubscribeResult = {
|
|
70
|
+
/** The provider's id for the registration; what `unsubscribe` receives. */
|
|
71
|
+
externalId: string;
|
|
72
|
+
/** The signing secret, if the provider issued one or accepted `input.secret`. */
|
|
73
|
+
secret?: string;
|
|
74
|
+
/** Anything `unsubscribe` needs later; handed back to it and to the next `subscribe`. */
|
|
75
|
+
state?: Record<string, unknown>;
|
|
76
|
+
/** The provider's starting change marker, for notifications that only say something changed. */
|
|
77
|
+
cursor?: string;
|
|
78
|
+
/** ISO time the provider says the registration lapses, read from its response. */
|
|
79
|
+
expiresAt?: string;
|
|
31
80
|
};
|
|
81
|
+
export type ZiteTriggerUnsubscribeInput = {
|
|
82
|
+
externalId: string;
|
|
83
|
+
state: Record<string, unknown>;
|
|
84
|
+
integration: string;
|
|
85
|
+
endpointId: string;
|
|
86
|
+
};
|
|
87
|
+
export type ZiteTriggerSubscribe = (input: ZiteTriggerSubscribeInput) => Promise<ZiteTriggerSubscribeResult> | ZiteTriggerSubscribeResult;
|
|
88
|
+
export type ZiteTriggerUnsubscribe = (input: ZiteTriggerUnsubscribeInput) => Promise<void> | void;
|
|
32
89
|
/**
|
|
33
90
|
* What a poll-triggered run receives as `input.__poll`. Only on a fire from
|
|
34
91
|
* the poll's schedule; a request call to the same endpoint carries none.
|
|
@@ -56,6 +113,8 @@ export type ZiteTriggerDelivery = {
|
|
|
56
113
|
/** Ids earlier deliveries returned as `handled`, newest last. Skip these. */
|
|
57
114
|
seen: string[];
|
|
58
115
|
};
|
|
116
|
+
/** What an app event trigger's run carries as `input.__webhook`: one event, no cursor. */
|
|
117
|
+
export type ZiteAppEventDelivery = Omit<ZiteTriggerDelivery, "cursor" | "seen">;
|
|
59
118
|
/**
|
|
60
119
|
* What a poll-triggered run, or a provider trigger's delivery, returns for
|
|
61
120
|
* the platform to keep: its new `cursor`, and the id of every item it acted
|
|
@@ -159,9 +218,13 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
|
|
|
159
218
|
validatesInput?: boolean;
|
|
160
219
|
execute: (params: {
|
|
161
220
|
input: TTrigger extends {
|
|
162
|
-
|
|
221
|
+
every: string;
|
|
163
222
|
} ? TInput & {
|
|
164
223
|
__poll?: ZitePollInput;
|
|
224
|
+
} : TTrigger extends {
|
|
225
|
+
events: string[];
|
|
226
|
+
} ? TInput & {
|
|
227
|
+
__webhook?: ZiteAppEventDelivery;
|
|
165
228
|
} : TTrigger extends {
|
|
166
229
|
integration: string;
|
|
167
230
|
} ? TInput & {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { createEndpoint, ZiteError } from "./index.js";
|
|
3
|
+
import { createEndpoint, ZiteError, } from "./index.js";
|
|
4
4
|
import { createTableClient } from "../runtime/index.js";
|
|
5
5
|
const requestContext = { user: { id: "u1", email: "a@b.c" } };
|
|
6
6
|
afterEach(() => {
|
|
@@ -134,7 +134,7 @@ describe("platform-triggered runs", () => {
|
|
|
134
134
|
it("hands a poll its cursor and seen ids, typed", async () => {
|
|
135
135
|
const endpoint = createEndpoint({
|
|
136
136
|
inputSchema: z.object({}),
|
|
137
|
-
trigger: {
|
|
137
|
+
trigger: { every: "5m", integration: "stripe" },
|
|
138
138
|
execute: async ({ input }) => {
|
|
139
139
|
const seen = new Set(input.__poll?.seen);
|
|
140
140
|
const items = ["a", "b"].filter((id) => !seen.has(id));
|
|
@@ -156,6 +156,32 @@ describe("platform-triggered runs", () => {
|
|
|
156
156
|
},
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
|
+
it("types a trigger's registration exports and checks what subscribe returns", async () => {
|
|
160
|
+
const subscribe = (async (input) => ({
|
|
161
|
+
externalId: `hook-for-${input.endpointId}`,
|
|
162
|
+
secret: input.secret,
|
|
163
|
+
state: { repo: "acme/api" },
|
|
164
|
+
}));
|
|
165
|
+
const unsubscribe = (async (input) => {
|
|
166
|
+
expect(input.state.repo).toBe("acme/api");
|
|
167
|
+
});
|
|
168
|
+
// @ts-expect-error subscribe has to return the provider's id
|
|
169
|
+
void (async () => ({ secret: "s" }));
|
|
170
|
+
const registered = await subscribe({
|
|
171
|
+
notificationUrl: "https://x",
|
|
172
|
+
integration: "github",
|
|
173
|
+
endpointId: "onPush",
|
|
174
|
+
secret: "abc",
|
|
175
|
+
previous: null,
|
|
176
|
+
});
|
|
177
|
+
expect(registered.externalId).toBe("hook-for-onPush");
|
|
178
|
+
await unsubscribe({
|
|
179
|
+
externalId: registered.externalId,
|
|
180
|
+
state: registered.state ?? {},
|
|
181
|
+
integration: "github",
|
|
182
|
+
endpointId: "onPush",
|
|
183
|
+
});
|
|
184
|
+
});
|
|
159
185
|
it("hands a provider trigger's delivery its cursor and seen ids, typed", async () => {
|
|
160
186
|
const endpoint = createEndpoint({
|
|
161
187
|
inputSchema: z.object({}),
|
|
@@ -173,6 +199,32 @@ describe("platform-triggered runs", () => {
|
|
|
173
199
|
context: { user: null },
|
|
174
200
|
})).resolves.toEqual({ cursor: "sync1+1", handled: ["e2"] });
|
|
175
201
|
});
|
|
202
|
+
it("types an app event trigger's run as one event, with no cursor to keep", async () => {
|
|
203
|
+
const endpoint = createEndpoint({
|
|
204
|
+
inputSchema: z.object({ event: z.object({ text: z.string() }) }),
|
|
205
|
+
trigger: { integration: "team-slack", events: ["message.channels"] },
|
|
206
|
+
execute: async ({ input }) => {
|
|
207
|
+
// @ts-expect-error an app event delivery has no cursor
|
|
208
|
+
void input.__webhook?.cursor;
|
|
209
|
+
return { id: input.__webhook?.deliveryId ?? null };
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
await expect(endpoint.execute({
|
|
213
|
+
input: {
|
|
214
|
+
event: { text: "hi" },
|
|
215
|
+
__webhook: { headers: {}, deliveryId: "slack:Ev1" },
|
|
216
|
+
},
|
|
217
|
+
context: { user: null },
|
|
218
|
+
})).resolves.toEqual({ id: "slack:Ev1" });
|
|
219
|
+
});
|
|
220
|
+
it("refuses events on a trigger that also polls", () => {
|
|
221
|
+
createEndpoint({
|
|
222
|
+
inputSchema: z.object({}),
|
|
223
|
+
// @ts-expect-error a poll can't also take events
|
|
224
|
+
trigger: { every: "5m", events: ["message.channels"] },
|
|
225
|
+
execute: async () => ({}),
|
|
226
|
+
});
|
|
227
|
+
});
|
|
176
228
|
it("does not validate a webhook fire", async () => {
|
|
177
229
|
const endpoint = createEndpoint({
|
|
178
230
|
inputSchema: z.object({ token: z.string().min(10) }),
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -1217,10 +1217,13 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1217
1217
|
"// Auto-generated type-narrowing wrapper. Do not edit manually.",
|
|
1218
1218
|
"// Re-exports createEndpoint with context.user typed to the app User.",
|
|
1219
1219
|
"",
|
|
1220
|
-
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteTrigger, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1220
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteTrigger, ZiteTriggerDelivery, ZiteAppEventDelivery, ZitePollInput, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1221
1221
|
"import type { User } from 'zitejs/auth';",
|
|
1222
1222
|
"",
|
|
1223
1223
|
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1224
|
+
// A trigger's registration exports are typed against these, and they
|
|
1225
|
+
// only reach app code through this file.
|
|
1226
|
+
"export type { ZiteAppEventDelivery, ZitePollInput, ZitePollResult, ZiteTriggerDelivery, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1224
1227
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1225
1228
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1226
1229
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1307,11 +1310,13 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1307
1310
|
" schedule?: TSchedule;",
|
|
1308
1311
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1309
1312
|
" webhook?: TWebhook;",
|
|
1310
|
-
" /** When set, a
|
|
1313
|
+
" /** When set, a connection's events (registered by this file's own `subscribe` export) or a poll (`every`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1311
1314
|
" trigger?: TTrigger;",
|
|
1312
1315
|
" execute: (",
|
|
1313
1316
|
" params: {",
|
|
1314
|
-
|
|
1317
|
+
// Mirrors the base module: a poll's run carries `__poll`, a provider
|
|
1318
|
+
// trigger's delivery `__webhook`.
|
|
1319
|
+
" input: TTrigger extends { every: string } ? TInput & { __poll?: ZitePollInput } : TTrigger extends { events: string[] } ? TInput & { __webhook?: ZiteAppEventDelivery } : TTrigger extends { integration: string } ? TInput & { __webhook?: ZiteTriggerDelivery } : TInput;",
|
|
1315
1320
|
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
1316
1321
|
" } & (TStream extends true ? { stream: ZiteStreamInterface } : {}),",
|
|
1317
1322
|
" ) => Promise<TOutput> | TOutput;",
|
|
@@ -382,6 +382,14 @@ describe('generateBackendWrapperTs', () => {
|
|
|
382
382
|
expect(output).toContain('TTrigger extends ZiteTrigger | undefined = undefined');
|
|
383
383
|
expect(output).toContain('ZiteTrigger, ZiteWebhook }');
|
|
384
384
|
});
|
|
385
|
+
it('passes the trigger registration types and trigger inputs through to app code', () => {
|
|
386
|
+
expect(output).toContain('ZiteTriggerSubscribe, ZiteTriggerSubscribeInput');
|
|
387
|
+
expect(output).toContain('ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput');
|
|
388
|
+
expect(output).toContain("TTrigger extends { every: string } ? TInput & { __poll?: ZitePollInput }");
|
|
389
|
+
expect(output).toContain('TInput & { __webhook?: ZiteTriggerDelivery }');
|
|
390
|
+
expect(output).toContain("TTrigger extends { events: string[] } ? TInput & { __webhook?: ZiteAppEventDelivery }");
|
|
391
|
+
expect(output).not.toContain('src/triggers/');
|
|
392
|
+
});
|
|
385
393
|
it('exports ZiteError', () => {
|
|
386
394
|
expect(output).toContain('ZiteError');
|
|
387
395
|
});
|