zitejs 0.9.138 → 0.9.140
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 +83 -1
- package/dist/cjs/backend/index.test.js +52 -0
- package/dist/cjs/integrations/rules.d.ts +2 -0
- package/dist/cjs/integrations/rules.js +3 -0
- package/dist/cjs/sync/lib.js +5 -5
- package/dist/esm/backend/index.d.ts +83 -1
- package/dist/esm/backend/index.test.js +52 -0
- package/dist/esm/integrations/rules.d.ts +2 -0
- package/dist/esm/integrations/rules.js +3 -0
- package/dist/esm/sync/lib.js +5 -5
- package/package.json +1 -1
|
@@ -23,6 +23,8 @@ export type ZiteTrigger = {
|
|
|
23
23
|
paused?: boolean;
|
|
24
24
|
every?: never;
|
|
25
25
|
events?: never;
|
|
26
|
+
table?: never;
|
|
27
|
+
form?: never;
|
|
26
28
|
} | {
|
|
27
29
|
/**
|
|
28
30
|
* The connection's name in `zite.config.json`, for a provider whose
|
|
@@ -37,6 +39,8 @@ export type ZiteTrigger = {
|
|
|
37
39
|
label?: string;
|
|
38
40
|
paused?: boolean;
|
|
39
41
|
every?: never;
|
|
42
|
+
table?: never;
|
|
43
|
+
form?: never;
|
|
40
44
|
} | {
|
|
41
45
|
/** `<n>m` (1–59) or `<n>h` (1–23). What makes the trigger a poll. */
|
|
42
46
|
every: string;
|
|
@@ -46,6 +50,74 @@ export type ZiteTrigger = {
|
|
|
46
50
|
label?: string;
|
|
47
51
|
paused?: boolean;
|
|
48
52
|
events?: never;
|
|
53
|
+
table?: never;
|
|
54
|
+
form?: never;
|
|
55
|
+
} | {
|
|
56
|
+
/**
|
|
57
|
+
* A table in the project's own database, as `zite.<table>` names it.
|
|
58
|
+
* Every write to it reaches the trigger, from apps, forms, imports, the
|
|
59
|
+
* database view and other automations; nothing is registered.
|
|
60
|
+
*/
|
|
61
|
+
table: string;
|
|
62
|
+
on: ZiteDatabaseTriggerEvent[];
|
|
63
|
+
/** Narrows `"updated"` to updates that changed one of these fields. */
|
|
64
|
+
fields?: string[];
|
|
65
|
+
/** The sentence the editor shows, e.g. "When an order ships". */
|
|
66
|
+
label?: string;
|
|
67
|
+
paused?: boolean;
|
|
68
|
+
integration?: never;
|
|
69
|
+
every?: never;
|
|
70
|
+
events?: never;
|
|
71
|
+
form?: never;
|
|
72
|
+
} | {
|
|
73
|
+
/**
|
|
74
|
+
* One of the project's own Fillout forms, by its name as the project
|
|
75
|
+
* lists it, or its id. Each finished live submission fires the
|
|
76
|
+
* endpoint; nothing is registered.
|
|
77
|
+
*/
|
|
78
|
+
form: string;
|
|
79
|
+
on: "submitted"[];
|
|
80
|
+
/** The sentence the editor shows, e.g. "When someone fills in the intake form". */
|
|
81
|
+
label?: string;
|
|
82
|
+
paused?: boolean;
|
|
83
|
+
integration?: never;
|
|
84
|
+
every?: never;
|
|
85
|
+
events?: never;
|
|
86
|
+
table?: never;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* What a form trigger's run gets as `input`: one submission. `answers` is
|
|
90
|
+
* keyed by question name; `submission` is the whole export, as the Fillout
|
|
91
|
+
* API returns it (`questions`, `calculations`, `urlParameters`, …).
|
|
92
|
+
*/
|
|
93
|
+
export type ZiteFormTriggerInput = {
|
|
94
|
+
event: "submitted";
|
|
95
|
+
form: string;
|
|
96
|
+
formId: string;
|
|
97
|
+
submissionId: string;
|
|
98
|
+
submittedAt: string | null;
|
|
99
|
+
answers: Record<string, unknown>;
|
|
100
|
+
submission: Record<string, unknown> & {
|
|
101
|
+
submissionId: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export type ZiteDatabaseTriggerEvent = "created" | "updated" | "deleted";
|
|
105
|
+
/**
|
|
106
|
+
* What a database trigger's run gets as `input`: one changed record. Field
|
|
107
|
+
* keys are the SDK's, as `zite.<table>.findOne` returns them; cast `record`
|
|
108
|
+
* and `previous` to the table's generated `<Table>RecordType` from
|
|
109
|
+
* `zitejs/db` to type their fields.
|
|
110
|
+
*/
|
|
111
|
+
export type ZiteDatabaseTriggerInput<TRecord extends Record<string, unknown> = Record<string, unknown>> = {
|
|
112
|
+
event: ZiteDatabaseTriggerEvent;
|
|
113
|
+
table: string;
|
|
114
|
+
recordId: string;
|
|
115
|
+
/** Null if it was deleted before the run read it; on `"deleted"`, the record as it was. */
|
|
116
|
+
record: TRecord | null;
|
|
117
|
+
/** The record before an `"updated"`; null otherwise, and on a very large bulk write. */
|
|
118
|
+
previous: TRecord | null;
|
|
119
|
+
/** What an `"updated"` changed. */
|
|
120
|
+
changedFields: string[];
|
|
49
121
|
};
|
|
50
122
|
/**
|
|
51
123
|
* What a provider trigger's `subscribe` export receives. Write it as
|
|
@@ -115,10 +187,20 @@ export type ZiteTriggerDeliveryRun = ZiteTriggerRun & ZiteTriggerDeliveryInfo;
|
|
|
115
187
|
export type ZiteTriggerContext<TTrigger> = TTrigger extends {
|
|
116
188
|
every: string;
|
|
117
189
|
} ? ZiteTriggerRun : TTrigger extends {
|
|
190
|
+
table: string;
|
|
191
|
+
} | {
|
|
192
|
+
form: string;
|
|
193
|
+
} ? ZiteTriggerDeliveryInfo : TTrigger extends {
|
|
118
194
|
events: string[];
|
|
119
195
|
} ? ZiteTriggerDeliveryInfo : TTrigger extends {
|
|
120
196
|
integration: string;
|
|
121
197
|
} ? ZiteTriggerDeliveryRun : never;
|
|
198
|
+
/** A database or form trigger's runs get the change or submission as `input`. */
|
|
199
|
+
export type ZiteEndpointInput<TInput, TTrigger> = TTrigger extends {
|
|
200
|
+
table: string;
|
|
201
|
+
} ? ZiteDatabaseTriggerInput : TTrigger extends {
|
|
202
|
+
form: string;
|
|
203
|
+
} ? ZiteFormTriggerInput : TInput;
|
|
122
204
|
export interface ZiteRequestContext {
|
|
123
205
|
user: {
|
|
124
206
|
id: string;
|
|
@@ -215,7 +297,7 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
|
|
|
215
297
|
*/
|
|
216
298
|
validatesInput?: boolean;
|
|
217
299
|
execute: (params: {
|
|
218
|
-
input: TInput
|
|
300
|
+
input: ZiteEndpointInput<TInput, TTrigger>;
|
|
219
301
|
/**
|
|
220
302
|
* Durable steps. `step.run(name, fn)` runs `fn` once per logical run:
|
|
221
303
|
* its result is saved before anything after it executes, and when a
|
|
@@ -207,6 +207,58 @@ const stepStub = {
|
|
|
207
207
|
return params.trigger;
|
|
208
208
|
},
|
|
209
209
|
});
|
|
210
|
+
(0, index_js_1.createEndpoint)({
|
|
211
|
+
trigger: { table: "orders", on: ["updated"], fields: ["status"] },
|
|
212
|
+
execute: async ({ input, trigger }) => {
|
|
213
|
+
// @ts-expect-error a database change has no cursor API
|
|
214
|
+
trigger?.advance("x");
|
|
215
|
+
const status = input.record?.status;
|
|
216
|
+
return { status, changed: input.changedFields, at: input.event };
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
(0, index_js_1.createEndpoint)({
|
|
220
|
+
// @ts-expect-error a database trigger names its table, not a connection
|
|
221
|
+
trigger: { table: "orders", on: ["created"], integration: "x" },
|
|
222
|
+
execute: async () => 1,
|
|
223
|
+
});
|
|
224
|
+
(0, index_js_1.createEndpoint)({
|
|
225
|
+
trigger: { form: "Intake form", on: ["submitted"] },
|
|
226
|
+
execute: async ({ input, trigger }) => {
|
|
227
|
+
// @ts-expect-error a form submission has no cursor API
|
|
228
|
+
trigger?.advance("x");
|
|
229
|
+
return { email: input.answers.Email, id: input.submissionId };
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
(0, index_js_1.createEndpoint)({
|
|
233
|
+
// @ts-expect-error a form trigger watches submissions only
|
|
234
|
+
trigger: { form: "Intake form", on: ["updated"] },
|
|
235
|
+
execute: async () => 1,
|
|
236
|
+
});
|
|
237
|
+
(0, index_js_1.createEndpoint)({
|
|
238
|
+
// @ts-expect-error only created, updated and deleted
|
|
239
|
+
trigger: { table: "orders", on: ["archived"] },
|
|
240
|
+
execute: async () => 1,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
(0, vitest_1.it)("hands a database trigger its change without parsing it against the input schema", async () => {
|
|
244
|
+
const endpoint = (0, index_js_1.createEndpoint)({
|
|
245
|
+
inputSchema: zod_1.z.object({ orderId: zod_1.z.string() }),
|
|
246
|
+
trigger: { table: "orders", on: ["created"] },
|
|
247
|
+
execute: async ({ input }) => input.recordId,
|
|
248
|
+
});
|
|
249
|
+
await (0, vitest_1.expect)(endpoint.execute({
|
|
250
|
+
step: stepStub,
|
|
251
|
+
input: {
|
|
252
|
+
event: "created",
|
|
253
|
+
table: "orders",
|
|
254
|
+
recordId: "rec_1",
|
|
255
|
+
record: { id: "rec_1" },
|
|
256
|
+
previous: null,
|
|
257
|
+
changedFields: [],
|
|
258
|
+
},
|
|
259
|
+
context: { user: null },
|
|
260
|
+
trigger: { headers: {}, deliveryId: "db:e1:rec_1" },
|
|
261
|
+
})).resolves.toBe("rec_1");
|
|
210
262
|
});
|
|
211
263
|
(0, vitest_1.it)("types a trigger's registration exports and checks what subscribe returns", async () => {
|
|
212
264
|
const subscribe = (async (input) => ({
|
|
@@ -40,9 +40,11 @@ declare const FIELDS_BY_TYPE: {
|
|
|
40
40
|
apollo: readonly string[];
|
|
41
41
|
attio: readonly string[];
|
|
42
42
|
granola: readonly string[];
|
|
43
|
+
clickup: readonly string[];
|
|
43
44
|
dropbox: readonly string[];
|
|
44
45
|
amazonS3: readonly string[];
|
|
45
46
|
snowflake: readonly string[];
|
|
47
|
+
smartsuite: readonly string[];
|
|
46
48
|
};
|
|
47
49
|
/** A service type `integrations.get` serves: one used through its npm package with a token. */
|
|
48
50
|
export type CredentialServiceType = keyof typeof FIELDS_BY_TYPE;
|
|
@@ -49,6 +49,7 @@ const FIELDS_BY_TYPE = {
|
|
|
49
49
|
apollo: extras(),
|
|
50
50
|
attio: extras(),
|
|
51
51
|
granola: extras(),
|
|
52
|
+
clickup: extras(),
|
|
52
53
|
dropbox: extras(),
|
|
53
54
|
// The IAM key alone reaches nothing: every call needs the region and the
|
|
54
55
|
// bucket, and S3-compatible providers need the endpoint too.
|
|
@@ -56,6 +57,8 @@ const FIELDS_BY_TYPE = {
|
|
|
56
57
|
// The SQL API lives on each account's own host, and the token is pinned to
|
|
57
58
|
// one role; the warehouse is optional and falls back to the user's default.
|
|
58
59
|
snowflake: extras("accountUrl", "role", "warehouse"),
|
|
60
|
+
// Every request names the workspace in an Account-Id header beside the key.
|
|
61
|
+
smartsuite: extras("workspaceId"),
|
|
59
62
|
};
|
|
60
63
|
/** Per service type: `accessToken`, then its extra labels sorted (Salesforce `instanceUrl`). */
|
|
61
64
|
exports.CREDENTIAL_FIELDS = FIELDS_BY_TYPE;
|
package/dist/cjs/sync/lib.js
CHANGED
|
@@ -524,7 +524,7 @@ function generateDbTs(inputSchema) {
|
|
|
524
524
|
lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', "// empty `notIn: []` applies no filter.");
|
|
525
525
|
lines.push("// sort is currently IGNORED here — it is stripped before the request.", "// (it does work on zite.auth.findAllUsers). Order in SQL instead.");
|
|
526
526
|
lines.push("// limit defaults to 500, max 2000; offset is a row count (a number)");
|
|
527
|
-
lines.push(
|
|
527
|
+
lines.push("// a filter value of `undefined` is REJECTED (it would query unfiltered", "// and match an arbitrary record). Check the value first.");
|
|
528
528
|
lines.push("// .findOne({ id?, filters?, fields? }) → T | undefined");
|
|
529
529
|
lines.push("// .create({ record }) → T");
|
|
530
530
|
lines.push("// .update({ id, record }) → { id: string, fields: Partial<T> }");
|
|
@@ -1232,13 +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, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1235
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteEndpointInput, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1236
1236
|
"import type { User } from 'zitejs/auth';",
|
|
1237
1237
|
"",
|
|
1238
1238
|
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1239
1239
|
// A trigger's registration exports are typed against these, and they
|
|
1240
1240
|
// only reach app code through this file.
|
|
1241
|
-
"export type { ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1241
|
+
"export type { ZiteDatabaseTriggerEvent, ZiteDatabaseTriggerInput, ZiteFormTriggerInput, ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1242
1242
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1243
1243
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1244
1244
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1326,11 +1326,11 @@ function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1326
1326
|
" schedule?: TSchedule;",
|
|
1327
1327
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1328
1328
|
" webhook?: TWebhook;",
|
|
1329
|
-
" /** When set, a connection's events (registered by this file's own `subscribe` export)
|
|
1329
|
+
" /** When set, a connection's events (registered by this file's own `subscribe` export), a poll (`every`), a change to one of the project's tables (`table`) or a submission to one of its forms (`form`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1330
1330
|
" trigger?: TTrigger;",
|
|
1331
1331
|
" execute: (",
|
|
1332
1332
|
" params: {",
|
|
1333
|
-
" input: TInput
|
|
1333
|
+
" input: ZiteEndpointInput<TInput, TTrigger>;",
|
|
1334
1334
|
" /** Durable steps: step.run(name, fn) runs fn once per logical run — its result is saved before anything after it executes, and a resumed run replays completed steps from their saved results. Provided by the platform; never define a local stand-in. */",
|
|
1335
1335
|
" step: ZiteStep;",
|
|
1336
1336
|
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
@@ -23,6 +23,8 @@ export type ZiteTrigger = {
|
|
|
23
23
|
paused?: boolean;
|
|
24
24
|
every?: never;
|
|
25
25
|
events?: never;
|
|
26
|
+
table?: never;
|
|
27
|
+
form?: never;
|
|
26
28
|
} | {
|
|
27
29
|
/**
|
|
28
30
|
* The connection's name in `zite.config.json`, for a provider whose
|
|
@@ -37,6 +39,8 @@ export type ZiteTrigger = {
|
|
|
37
39
|
label?: string;
|
|
38
40
|
paused?: boolean;
|
|
39
41
|
every?: never;
|
|
42
|
+
table?: never;
|
|
43
|
+
form?: never;
|
|
40
44
|
} | {
|
|
41
45
|
/** `<n>m` (1–59) or `<n>h` (1–23). What makes the trigger a poll. */
|
|
42
46
|
every: string;
|
|
@@ -46,6 +50,74 @@ export type ZiteTrigger = {
|
|
|
46
50
|
label?: string;
|
|
47
51
|
paused?: boolean;
|
|
48
52
|
events?: never;
|
|
53
|
+
table?: never;
|
|
54
|
+
form?: never;
|
|
55
|
+
} | {
|
|
56
|
+
/**
|
|
57
|
+
* A table in the project's own database, as `zite.<table>` names it.
|
|
58
|
+
* Every write to it reaches the trigger, from apps, forms, imports, the
|
|
59
|
+
* database view and other automations; nothing is registered.
|
|
60
|
+
*/
|
|
61
|
+
table: string;
|
|
62
|
+
on: ZiteDatabaseTriggerEvent[];
|
|
63
|
+
/** Narrows `"updated"` to updates that changed one of these fields. */
|
|
64
|
+
fields?: string[];
|
|
65
|
+
/** The sentence the editor shows, e.g. "When an order ships". */
|
|
66
|
+
label?: string;
|
|
67
|
+
paused?: boolean;
|
|
68
|
+
integration?: never;
|
|
69
|
+
every?: never;
|
|
70
|
+
events?: never;
|
|
71
|
+
form?: never;
|
|
72
|
+
} | {
|
|
73
|
+
/**
|
|
74
|
+
* One of the project's own Fillout forms, by its name as the project
|
|
75
|
+
* lists it, or its id. Each finished live submission fires the
|
|
76
|
+
* endpoint; nothing is registered.
|
|
77
|
+
*/
|
|
78
|
+
form: string;
|
|
79
|
+
on: "submitted"[];
|
|
80
|
+
/** The sentence the editor shows, e.g. "When someone fills in the intake form". */
|
|
81
|
+
label?: string;
|
|
82
|
+
paused?: boolean;
|
|
83
|
+
integration?: never;
|
|
84
|
+
every?: never;
|
|
85
|
+
events?: never;
|
|
86
|
+
table?: never;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* What a form trigger's run gets as `input`: one submission. `answers` is
|
|
90
|
+
* keyed by question name; `submission` is the whole export, as the Fillout
|
|
91
|
+
* API returns it (`questions`, `calculations`, `urlParameters`, …).
|
|
92
|
+
*/
|
|
93
|
+
export type ZiteFormTriggerInput = {
|
|
94
|
+
event: "submitted";
|
|
95
|
+
form: string;
|
|
96
|
+
formId: string;
|
|
97
|
+
submissionId: string;
|
|
98
|
+
submittedAt: string | null;
|
|
99
|
+
answers: Record<string, unknown>;
|
|
100
|
+
submission: Record<string, unknown> & {
|
|
101
|
+
submissionId: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export type ZiteDatabaseTriggerEvent = "created" | "updated" | "deleted";
|
|
105
|
+
/**
|
|
106
|
+
* What a database trigger's run gets as `input`: one changed record. Field
|
|
107
|
+
* keys are the SDK's, as `zite.<table>.findOne` returns them; cast `record`
|
|
108
|
+
* and `previous` to the table's generated `<Table>RecordType` from
|
|
109
|
+
* `zitejs/db` to type their fields.
|
|
110
|
+
*/
|
|
111
|
+
export type ZiteDatabaseTriggerInput<TRecord extends Record<string, unknown> = Record<string, unknown>> = {
|
|
112
|
+
event: ZiteDatabaseTriggerEvent;
|
|
113
|
+
table: string;
|
|
114
|
+
recordId: string;
|
|
115
|
+
/** Null if it was deleted before the run read it; on `"deleted"`, the record as it was. */
|
|
116
|
+
record: TRecord | null;
|
|
117
|
+
/** The record before an `"updated"`; null otherwise, and on a very large bulk write. */
|
|
118
|
+
previous: TRecord | null;
|
|
119
|
+
/** What an `"updated"` changed. */
|
|
120
|
+
changedFields: string[];
|
|
49
121
|
};
|
|
50
122
|
/**
|
|
51
123
|
* What a provider trigger's `subscribe` export receives. Write it as
|
|
@@ -115,10 +187,20 @@ export type ZiteTriggerDeliveryRun = ZiteTriggerRun & ZiteTriggerDeliveryInfo;
|
|
|
115
187
|
export type ZiteTriggerContext<TTrigger> = TTrigger extends {
|
|
116
188
|
every: string;
|
|
117
189
|
} ? ZiteTriggerRun : TTrigger extends {
|
|
190
|
+
table: string;
|
|
191
|
+
} | {
|
|
192
|
+
form: string;
|
|
193
|
+
} ? ZiteTriggerDeliveryInfo : TTrigger extends {
|
|
118
194
|
events: string[];
|
|
119
195
|
} ? ZiteTriggerDeliveryInfo : TTrigger extends {
|
|
120
196
|
integration: string;
|
|
121
197
|
} ? ZiteTriggerDeliveryRun : never;
|
|
198
|
+
/** A database or form trigger's runs get the change or submission as `input`. */
|
|
199
|
+
export type ZiteEndpointInput<TInput, TTrigger> = TTrigger extends {
|
|
200
|
+
table: string;
|
|
201
|
+
} ? ZiteDatabaseTriggerInput : TTrigger extends {
|
|
202
|
+
form: string;
|
|
203
|
+
} ? ZiteFormTriggerInput : TInput;
|
|
122
204
|
export interface ZiteRequestContext {
|
|
123
205
|
user: {
|
|
124
206
|
id: string;
|
|
@@ -215,7 +297,7 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
|
|
|
215
297
|
*/
|
|
216
298
|
validatesInput?: boolean;
|
|
217
299
|
execute: (params: {
|
|
218
|
-
input: TInput
|
|
300
|
+
input: ZiteEndpointInput<TInput, TTrigger>;
|
|
219
301
|
/**
|
|
220
302
|
* Durable steps. `step.run(name, fn)` runs `fn` once per logical run:
|
|
221
303
|
* its result is saved before anything after it executes, and when a
|
|
@@ -205,6 +205,58 @@ describe("platform-triggered runs", () => {
|
|
|
205
205
|
return params.trigger;
|
|
206
206
|
},
|
|
207
207
|
});
|
|
208
|
+
createEndpoint({
|
|
209
|
+
trigger: { table: "orders", on: ["updated"], fields: ["status"] },
|
|
210
|
+
execute: async ({ input, trigger }) => {
|
|
211
|
+
// @ts-expect-error a database change has no cursor API
|
|
212
|
+
trigger?.advance("x");
|
|
213
|
+
const status = input.record?.status;
|
|
214
|
+
return { status, changed: input.changedFields, at: input.event };
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
createEndpoint({
|
|
218
|
+
// @ts-expect-error a database trigger names its table, not a connection
|
|
219
|
+
trigger: { table: "orders", on: ["created"], integration: "x" },
|
|
220
|
+
execute: async () => 1,
|
|
221
|
+
});
|
|
222
|
+
createEndpoint({
|
|
223
|
+
trigger: { form: "Intake form", on: ["submitted"] },
|
|
224
|
+
execute: async ({ input, trigger }) => {
|
|
225
|
+
// @ts-expect-error a form submission has no cursor API
|
|
226
|
+
trigger?.advance("x");
|
|
227
|
+
return { email: input.answers.Email, id: input.submissionId };
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
createEndpoint({
|
|
231
|
+
// @ts-expect-error a form trigger watches submissions only
|
|
232
|
+
trigger: { form: "Intake form", on: ["updated"] },
|
|
233
|
+
execute: async () => 1,
|
|
234
|
+
});
|
|
235
|
+
createEndpoint({
|
|
236
|
+
// @ts-expect-error only created, updated and deleted
|
|
237
|
+
trigger: { table: "orders", on: ["archived"] },
|
|
238
|
+
execute: async () => 1,
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
it("hands a database trigger its change without parsing it against the input schema", async () => {
|
|
242
|
+
const endpoint = createEndpoint({
|
|
243
|
+
inputSchema: z.object({ orderId: z.string() }),
|
|
244
|
+
trigger: { table: "orders", on: ["created"] },
|
|
245
|
+
execute: async ({ input }) => input.recordId,
|
|
246
|
+
});
|
|
247
|
+
await expect(endpoint.execute({
|
|
248
|
+
step: stepStub,
|
|
249
|
+
input: {
|
|
250
|
+
event: "created",
|
|
251
|
+
table: "orders",
|
|
252
|
+
recordId: "rec_1",
|
|
253
|
+
record: { id: "rec_1" },
|
|
254
|
+
previous: null,
|
|
255
|
+
changedFields: [],
|
|
256
|
+
},
|
|
257
|
+
context: { user: null },
|
|
258
|
+
trigger: { headers: {}, deliveryId: "db:e1:rec_1" },
|
|
259
|
+
})).resolves.toBe("rec_1");
|
|
208
260
|
});
|
|
209
261
|
it("types a trigger's registration exports and checks what subscribe returns", async () => {
|
|
210
262
|
const subscribe = (async (input) => ({
|
|
@@ -40,9 +40,11 @@ declare const FIELDS_BY_TYPE: {
|
|
|
40
40
|
apollo: readonly string[];
|
|
41
41
|
attio: readonly string[];
|
|
42
42
|
granola: readonly string[];
|
|
43
|
+
clickup: readonly string[];
|
|
43
44
|
dropbox: readonly string[];
|
|
44
45
|
amazonS3: readonly string[];
|
|
45
46
|
snowflake: readonly string[];
|
|
47
|
+
smartsuite: readonly string[];
|
|
46
48
|
};
|
|
47
49
|
/** A service type `integrations.get` serves: one used through its npm package with a token. */
|
|
48
50
|
export type CredentialServiceType = keyof typeof FIELDS_BY_TYPE;
|
|
@@ -46,6 +46,7 @@ const FIELDS_BY_TYPE = {
|
|
|
46
46
|
apollo: extras(),
|
|
47
47
|
attio: extras(),
|
|
48
48
|
granola: extras(),
|
|
49
|
+
clickup: extras(),
|
|
49
50
|
dropbox: extras(),
|
|
50
51
|
// The IAM key alone reaches nothing: every call needs the region and the
|
|
51
52
|
// bucket, and S3-compatible providers need the endpoint too.
|
|
@@ -53,6 +54,8 @@ const FIELDS_BY_TYPE = {
|
|
|
53
54
|
// The SQL API lives on each account's own host, and the token is pinned to
|
|
54
55
|
// one role; the warehouse is optional and falls back to the user's default.
|
|
55
56
|
snowflake: extras("accountUrl", "role", "warehouse"),
|
|
57
|
+
// Every request names the workspace in an Account-Id header beside the key.
|
|
58
|
+
smartsuite: extras("workspaceId"),
|
|
56
59
|
};
|
|
57
60
|
/** Per service type: `accessToken`, then its extra labels sorted (Salesforce `instanceUrl`). */
|
|
58
61
|
export const CREDENTIAL_FIELDS = FIELDS_BY_TYPE;
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -509,7 +509,7 @@ export function generateDbTs(inputSchema) {
|
|
|
509
509
|
lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', "// empty `notIn: []` applies no filter.");
|
|
510
510
|
lines.push("// sort is currently IGNORED here — it is stripped before the request.", "// (it does work on zite.auth.findAllUsers). Order in SQL instead.");
|
|
511
511
|
lines.push("// limit defaults to 500, max 2000; offset is a row count (a number)");
|
|
512
|
-
lines.push(
|
|
512
|
+
lines.push("// a filter value of `undefined` is REJECTED (it would query unfiltered", "// and match an arbitrary record). Check the value first.");
|
|
513
513
|
lines.push("// .findOne({ id?, filters?, fields? }) → T | undefined");
|
|
514
514
|
lines.push("// .create({ record }) → T");
|
|
515
515
|
lines.push("// .update({ id, record }) → { id: string, fields: Partial<T> }");
|
|
@@ -1217,13 +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, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1220
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteEndpointInput, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1221
1221
|
"import type { User } from 'zitejs/auth';",
|
|
1222
1222
|
"",
|
|
1223
1223
|
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1224
1224
|
// A trigger's registration exports are typed against these, and they
|
|
1225
1225
|
// only reach app code through this file.
|
|
1226
|
-
"export type { ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1226
|
+
"export type { ZiteDatabaseTriggerEvent, ZiteDatabaseTriggerInput, ZiteFormTriggerInput, ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1227
1227
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1228
1228
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1229
1229
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1311,11 +1311,11 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1311
1311
|
" schedule?: TSchedule;",
|
|
1312
1312
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1313
1313
|
" webhook?: TWebhook;",
|
|
1314
|
-
" /** When set, a connection's events (registered by this file's own `subscribe` export)
|
|
1314
|
+
" /** When set, a connection's events (registered by this file's own `subscribe` export), a poll (`every`), a change to one of the project's tables (`table`) or a submission to one of its forms (`form`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1315
1315
|
" trigger?: TTrigger;",
|
|
1316
1316
|
" execute: (",
|
|
1317
1317
|
" params: {",
|
|
1318
|
-
" input: TInput
|
|
1318
|
+
" input: ZiteEndpointInput<TInput, TTrigger>;",
|
|
1319
1319
|
" /** Durable steps: step.run(name, fn) runs fn once per logical run — its result is saved before anything after it executes, and a resumed run replays completed steps from their saved results. Provided by the platform; never define a local stand-in. */",
|
|
1320
1320
|
" step: ZiteStep;",
|
|
1321
1321
|
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|