zitejs 0.9.137 → 0.9.139

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/README.md CHANGED
@@ -23,10 +23,15 @@ const result = await zite.sql({
23
23
  query: 'SELECT "id", "email" FROM "ziteUsers"',
24
24
  });
25
25
 
26
- // Define API endpoints
26
+ // Define API endpoints. step.run saves each result as it completes, so a
27
+ // failed run resumed from run history skips the steps that already ran.
27
28
  import { createEndpoint, ZiteError } from 'zitejs/api';
28
29
  export default createEndpoint({
29
- execute: async ({ input }) => { ... },
30
+ execute: async ({ input, step }) => {
31
+ const order = await step.run('load-order', () => loadOrder(input.id));
32
+ await step.run('charge', () => charge(order));
33
+ return { charged: true };
34
+ },
30
35
  });
31
36
 
32
37
  // Authentication
@@ -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) => ({
@@ -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('// a filter value of `undefined` is REJECTED (it would query unfiltered', '// and match an arbitrary record). Check the value first.');
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, 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
- "export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
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,13 @@ 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) or a poll (`every`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
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
+ " /** 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
+ " step: ZiteStep;",
1334
1336
  " context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
1335
1337
  // Mirrors the base module: a trigger's run gets `trigger`.
1336
1338
  " } & (TStream extends true ? { stream: ZiteStreamInterface } : {}) & (TTrigger extends ZiteTrigger ? { trigger?: ZiteTriggerContext<TTrigger> } : {}),",
@@ -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) => ({
@@ -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('// a filter value of `undefined` is REJECTED (it would query unfiltered', '// and match an arbitrary record). Check the value first.');
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, 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
- "export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
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,13 @@ 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) or a poll (`every`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
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
+ " /** 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
+ " step: ZiteStep;",
1319
1321
  " context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
1320
1322
  // Mirrors the base module: a trigger's run gets `trigger`.
1321
1323
  " } & (TStream extends true ? { stream: ZiteStreamInterface } : {}) & (TTrigger extends ZiteTrigger ? { trigger?: ZiteTriggerContext<TTrigger> } : {}),",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.137",
3
+ "version": "0.9.139",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "sideEffects": false,