zitejs 0.9.125 → 0.9.126

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.
@@ -13,17 +13,59 @@ export type ZiteWebhook = {
13
13
  export type ZiteTrigger = {
14
14
  /**
15
15
  * The connection's name in `zite.config.json`. Registered with the
16
- * provider by `src/triggers/<endpointId>/subscribe.ts` and taken down
17
- * by `unsubscribe.ts`, which the platform runs at publish and teardown.
16
+ * provider by the endpoint file's own `subscribe` export and taken down
17
+ * by its `unsubscribe` export, which the platform runs at publish and
18
+ * teardown.
18
19
  */
19
20
  integration: string;
21
+ /** The sentence the editor shows for what fires this endpoint, e.g. "When a task is completed in Asana". */
22
+ label?: string;
20
23
  paused?: boolean;
21
24
  } | {
22
25
  provider: "poll";
23
26
  /** `<n>m` (1–59) or `<n>h` (1–23) */
24
27
  every: string;
28
+ /** The sentence the editor shows for this poll, e.g. "Every 10 minutes, new Stripe charges". */
29
+ label?: string;
25
30
  paused?: boolean;
26
31
  };
32
+ /**
33
+ * What a poll-triggered run receives as `input.__poll`. Only on a fire from
34
+ * the poll's schedule; a request call to the same endpoint carries none.
35
+ */
36
+ export type ZitePollInput = {
37
+ /** What the previous successful run returned as `cursor`; null on the first run. */
38
+ cursor: string | null;
39
+ /** Ids earlier runs returned as `handled`, newest last. Skip these. */
40
+ seen: string[];
41
+ };
42
+ /**
43
+ * What a provider trigger's delivery carries as `input.__webhook`, beside the
44
+ * provider's own body. `cursor` and `seen` work as they do for a poll: for a
45
+ * provider whose notification only says that something changed, `subscribe`
46
+ * returns a starting `cursor` and each delivery returns the next one. A
47
+ * trigger that keeps a cursor gets one delivery at a time.
48
+ */
49
+ export type ZiteTriggerDelivery = {
50
+ headers: Record<string, string>;
51
+ /** The provider's id for this delivery; redeliveries share it. */
52
+ deliveryId?: string;
53
+ provider?: string;
54
+ /** What `subscribe` or the previous successful delivery returned as `cursor`. */
55
+ cursor: string | null;
56
+ /** Ids earlier deliveries returned as `handled`, newest last. Skip these. */
57
+ seen: string[];
58
+ };
59
+ /**
60
+ * What a poll-triggered run, or a provider trigger's delivery, returns for
61
+ * the platform to keep: its new `cursor`, and the id of every item it acted
62
+ * on, so the next run's `seen` includes them even if the cursor hands them
63
+ * back.
64
+ */
65
+ export type ZitePollResult = {
66
+ cursor?: string;
67
+ handled?: string[];
68
+ };
27
69
  export interface ZiteRequestContext {
28
70
  user: {
29
71
  id: string;
@@ -116,7 +158,15 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
116
158
  */
117
159
  validatesInput?: boolean;
118
160
  execute: (params: {
119
- input: TInput;
161
+ input: TTrigger extends {
162
+ provider: "poll";
163
+ } ? TInput & {
164
+ __poll?: ZitePollInput;
165
+ } : TTrigger extends {
166
+ integration: string;
167
+ } ? TInput & {
168
+ __webhook?: ZiteTriggerDelivery;
169
+ } : TInput;
120
170
  context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;
121
171
  } & (TStream extends true ? {
122
172
  stream: ZiteStreamInterface;
@@ -133,6 +133,48 @@ const requestContext = { user: { id: "u1", email: "a@b.c" } };
133
133
  });
134
134
  await (0, vitest_1.expect)(endpoint.execute({ input: undefined, context: requestContext })).rejects.toMatchObject({ code: "BAD_REQUEST" });
135
135
  });
136
+ (0, vitest_1.it)("hands a poll its cursor and seen ids, typed", async () => {
137
+ const endpoint = (0, index_js_1.createEndpoint)({
138
+ inputSchema: zod_1.z.object({}),
139
+ trigger: { provider: "poll", every: "5m" },
140
+ execute: async ({ input }) => {
141
+ const seen = new Set(input.__poll?.seen);
142
+ const items = ["a", "b"].filter((id) => !seen.has(id));
143
+ return { cursor: input.__poll?.cursor ?? "start", handled: items };
144
+ },
145
+ });
146
+ await (0, vitest_1.expect)(endpoint.execute({
147
+ input: { __poll: { cursor: "c1", seen: ["a"] } },
148
+ context: { user: null },
149
+ })).resolves.toEqual({ cursor: "c1", handled: ["b"] });
150
+ });
151
+ (0, vitest_1.it)("gives __poll only to poll triggers", () => {
152
+ (0, index_js_1.createEndpoint)({
153
+ inputSchema: zod_1.z.object({ action: zod_1.z.string() }),
154
+ trigger: { integration: "github" },
155
+ execute: async ({ input }) => {
156
+ // @ts-expect-error a provider trigger's input has no __poll
157
+ return input.__poll;
158
+ },
159
+ });
160
+ });
161
+ (0, vitest_1.it)("hands a provider trigger's delivery its cursor and seen ids, typed", async () => {
162
+ const endpoint = (0, index_js_1.createEndpoint)({
163
+ inputSchema: zod_1.z.object({}),
164
+ trigger: { integration: "googleCalendar" },
165
+ execute: async ({ input }) => {
166
+ const seen = new Set(input.__webhook?.seen);
167
+ const changed = ["e1", "e2"].filter((id) => !seen.has(id));
168
+ return { cursor: `${input.__webhook?.cursor}+1`, handled: changed };
169
+ },
170
+ });
171
+ await (0, vitest_1.expect)(endpoint.execute({
172
+ input: {
173
+ __webhook: { headers: {}, cursor: "sync1", seen: ["e1"] },
174
+ },
175
+ context: { user: null },
176
+ })).resolves.toEqual({ cursor: "sync1+1", handled: ["e2"] });
177
+ });
136
178
  (0, vitest_1.it)("does not validate a webhook fire", async () => {
137
179
  const endpoint = (0, index_js_1.createEndpoint)({
138
180
  inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/index.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -40,7 +40,7 @@ exports.runBundle = runBundle;
40
40
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
41
41
  *
42
42
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
43
- * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
43
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/index.ts), resolves SDK imports via .zite/backend.ts
44
44
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
45
45
  * code suitable for the cloudflare-lambda worker runtime.
46
46
  *
@@ -420,7 +420,7 @@ async function bundleEndpointsImpl(baseDir, root, endpointNames) {
420
420
  for (const name of endpointNames) {
421
421
  if (root === 'automations' && name !== sourceRoots_js_1.AUTOMATION_ENDPOINT_NAME) {
422
422
  endpointErrors[name] =
423
- `An automation is one workflow, src/workflow.ts, bundled as "${sourceRoots_js_1.AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
423
+ `An automation is one workflow, src/index.ts, bundled as "${sourceRoots_js_1.AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
424
424
  continue;
425
425
  }
426
426
  const entryFile = (0, sourceRoots_js_1.endpointEntryFile)(root, name);
package/dist/cjs/cli.js CHANGED
@@ -75,7 +75,7 @@ async function main() {
75
75
  console.error(' dev Run sync then watch for changes (like npx convex dev)');
76
76
  console.error(' generate Regenerate every app and automation .zite/ (monorepo)');
77
77
  console.error(' check Run tsc --noEmit (and vite build where there is one) for every app and automation');
78
- console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/workflow.ts) for cloudflare-lambda');
78
+ console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/index.ts) for cloudflare-lambda');
79
79
  process.exit(1);
80
80
  }
81
81
  }
@@ -0,0 +1,14 @@
1
+ export type ZiteProjectUser = {
2
+ uuid: string;
3
+ firstName: string | null;
4
+ lastName: string | null;
5
+ email: string;
6
+ profilePictureUrl: string | null;
7
+ };
8
+ export type MetaListUsersResult = {
9
+ users: ZiteProjectUser[];
10
+ };
11
+ export declare class ZiteMeta {
12
+ static listUsers(): Promise<MetaListUsersResult>;
13
+ }
14
+ export declare const Meta: typeof ZiteMeta;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Meta = exports.ZiteMeta = void 0;
4
+ const sdkCall_js_1 = require("../internal/sdkCall.js");
5
+ const META_SDK_INTEGRATION_ID = '__meta__';
6
+ class ZiteMeta {
7
+ static listUsers() {
8
+ return (0, sdkCall_js_1.getSdkCall)()(META_SDK_INTEGRATION_ID, 'ZiteMeta', 'listUsers', {});
9
+ }
10
+ }
11
+ exports.ZiteMeta = ZiteMeta;
12
+ exports.Meta = ZiteMeta;
@@ -0,0 +1,25 @@
1
+ export interface NotificationLink {
2
+ path?: string;
3
+ params?: Record<string, string>;
4
+ }
5
+ export type NotificationsCreateParams = {
6
+ recipients: string[];
7
+ title: string;
8
+ body?: string;
9
+ link?: NotificationLink;
10
+ path?: string;
11
+ params?: Record<string, string>;
12
+ payload?: Record<string, unknown>;
13
+ idempotencyKey?: string;
14
+ };
15
+ export type NotificationsCreateResult = {
16
+ created: number;
17
+ } | {
18
+ created: 0;
19
+ preview: true;
20
+ wouldCreate: number;
21
+ };
22
+ export declare class ZiteNotifications {
23
+ static create(params: NotificationsCreateParams): Promise<NotificationsCreateResult>;
24
+ }
25
+ export declare const Notifications: typeof ZiteNotifications;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Notifications = exports.ZiteNotifications = void 0;
4
+ const sdkCall_js_1 = require("../internal/sdkCall.js");
5
+ const NOTIFICATIONS_SDK_INTEGRATION_ID = '__notifications__';
6
+ class ZiteNotifications {
7
+ static create(params) {
8
+ return (0, sdkCall_js_1.getSdkCall)()(NOTIFICATIONS_SDK_INTEGRATION_ID, 'ZiteNotifications', 'create', params);
9
+ }
10
+ }
11
+ exports.ZiteNotifications = ZiteNotifications;
12
+ exports.Notifications = ZiteNotifications;
@@ -18,12 +18,12 @@ export declare function listSourceDirs(cwd?: string): SourceDir[];
18
18
  /** The source dir with this bare name, under whichever root holds it. */
19
19
  export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
20
20
  /**
21
- * An automation is one workflow: its single endpoint is `src/workflow.ts` and
21
+ * An automation is one workflow: its single endpoint is `src/index.ts` and
22
22
  * its name is fixed, so nothing about an automation is keyed on a file name
23
23
  * the way an app's `src/api/<name>.ts` endpoints are.
24
24
  */
25
- export declare const AUTOMATION_ENDPOINT_NAME = "workflow";
26
- export declare const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
25
+ export declare const AUTOMATION_ENDPOINT_NAME = "index";
26
+ export declare const AUTOMATION_ENTRY_FILE = "src/index.ts";
27
27
  /** The root a source dir sits under, read from its parent directory. */
28
28
  export declare function sourceRootOf(sourceDirPath: string): SourceRoot | undefined;
29
29
  /** An endpoint's entry file, relative to the source dir. */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AUTOMATION_WORKFLOW_FILE = exports.AUTOMATION_ENDPOINT_NAME = exports.SOURCE_ROOTS = void 0;
3
+ exports.AUTOMATION_ENTRY_FILE = exports.AUTOMATION_ENDPOINT_NAME = exports.SOURCE_ROOTS = void 0;
4
4
  exports.listSourceDirs = listSourceDirs;
5
5
  exports.findSourceDir = findSourceDir;
6
6
  exports.sourceRootOf = sourceRootOf;
@@ -35,12 +35,12 @@ function findSourceDir(name, cwd = ".") {
35
35
  return listSourceDirs(cwd).find((d) => d.dir === name);
36
36
  }
37
37
  /**
38
- * An automation is one workflow: its single endpoint is `src/workflow.ts` and
38
+ * An automation is one workflow: its single endpoint is `src/index.ts` and
39
39
  * its name is fixed, so nothing about an automation is keyed on a file name
40
40
  * the way an app's `src/api/<name>.ts` endpoints are.
41
41
  */
42
- exports.AUTOMATION_ENDPOINT_NAME = "workflow";
43
- exports.AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
42
+ exports.AUTOMATION_ENDPOINT_NAME = "index";
43
+ exports.AUTOMATION_ENTRY_FILE = "src/index.ts";
44
44
  /** The root a source dir sits under, read from its parent directory. */
45
45
  function sourceRootOf(sourceDirPath) {
46
46
  const parent = (0, path_1.basename)((0, path_1.dirname)((0, path_1.resolve)(sourceDirPath)));
@@ -51,13 +51,13 @@ function sourceRootOf(sourceDirPath) {
51
51
  /** An endpoint's entry file, relative to the source dir. */
52
52
  function endpointEntryFile(root, name) {
53
53
  return root === "automations"
54
- ? exports.AUTOMATION_WORKFLOW_FILE
54
+ ? exports.AUTOMATION_ENTRY_FILE
55
55
  : (0, path_1.join)("src", "api", `${name}.ts`);
56
56
  }
57
57
  /** Every endpoint name a source dir has on disk. */
58
58
  function listEndpointNames(root, sourceDirPath) {
59
59
  if (root === "automations") {
60
- return (0, fs_1.existsSync)((0, path_1.join)(sourceDirPath, exports.AUTOMATION_WORKFLOW_FILE))
60
+ return (0, fs_1.existsSync)((0, path_1.join)(sourceDirPath, exports.AUTOMATION_ENTRY_FILE))
61
61
  ? [exports.AUTOMATION_ENDPOINT_NAME]
62
62
  : [];
63
63
  }
@@ -11,9 +11,9 @@ const sourceRoots_js_1 = require("./sourceRoots.js");
11
11
  (0, vitest_1.expect)((0, sourceRoots_js_1.sourceRootOf)("/repo/automations/digest")).toBe("automations");
12
12
  (0, vitest_1.expect)((0, sourceRoots_js_1.sourceRootOf)("/repo/packages/ui")).toBeUndefined();
13
13
  });
14
- (0, vitest_1.it)("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/workflow.ts", () => {
14
+ (0, vitest_1.it)("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/index.ts", () => {
15
15
  (0, vitest_1.expect)((0, sourceRoots_js_1.endpointEntryFile)("apps", "listItems")).toBe("src/api/listItems.ts");
16
- (0, vitest_1.expect)((0, sourceRoots_js_1.endpointEntryFile)("automations", "workflow")).toBe("src/workflow.ts");
16
+ (0, vitest_1.expect)((0, sourceRoots_js_1.endpointEntryFile)("automations", "index")).toBe("src/index.ts");
17
17
  });
18
18
  (0, vitest_1.it)("lists what is on disk under each layout", () => {
19
19
  const repo = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "zitejs-roots-"));
@@ -22,9 +22,9 @@ const sourceRoots_js_1 = require("./sourceRoots.js");
22
22
  (0, fs_1.writeFileSync)((0, path_1.join)(repo, "apps/crm/src/api/apiTypes.d.ts"), "");
23
23
  (0, fs_1.mkdirSync)((0, path_1.join)(repo, "automations/digest/src"), { recursive: true });
24
24
  (0, fs_1.mkdirSync)((0, path_1.join)(repo, "automations/empty/src"), { recursive: true });
25
- (0, fs_1.writeFileSync)((0, path_1.join)(repo, "automations/digest/src/workflow.ts"), "");
25
+ (0, fs_1.writeFileSync)((0, path_1.join)(repo, "automations/digest/src/index.ts"), "");
26
26
  (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("apps", (0, path_1.join)(repo, "apps/crm"))).toEqual(["listItems"]);
27
- (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("automations", (0, path_1.join)(repo, "automations/digest"))).toEqual(["workflow"]);
27
+ (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("automations", (0, path_1.join)(repo, "automations/digest"))).toEqual(["index"]);
28
28
  (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("automations", (0, path_1.join)(repo, "automations/empty"))).toEqual([]);
29
29
  });
30
30
  });
@@ -1426,7 +1426,7 @@ function generateIntegrationsIndex(entries, opts) {
1426
1426
  "// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.",
1427
1427
  "// zitejs/integrations — an integration's credentials by its zite.config.json entry name.",
1428
1428
  "//",
1429
- "// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):",
1429
+ "// Usage in endpoints (src/api/*.ts) and automations (src/index.ts):",
1430
1430
  "// import { integrations } from 'zitejs/integrations';",
1431
1431
  "//",
1432
1432
  "// const crm = await integrations.get('<entry name>');",
@@ -465,7 +465,7 @@ const duplicateIdentifierErrorsIn = (source) => {
465
465
  "// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.
466
466
  // zitejs/integrations — an integration's credentials by its zite.config.json entry name.
467
467
  //
468
- // Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):
468
+ // Usage in endpoints (src/api/*.ts) and automations (src/index.ts):
469
469
  // import { integrations } from 'zitejs/integrations';
470
470
  //
471
471
  // const crm = await integrations.get('<entry name>');
@@ -13,17 +13,59 @@ export type ZiteWebhook = {
13
13
  export type ZiteTrigger = {
14
14
  /**
15
15
  * The connection's name in `zite.config.json`. Registered with the
16
- * provider by `src/triggers/<endpointId>/subscribe.ts` and taken down
17
- * by `unsubscribe.ts`, which the platform runs at publish and teardown.
16
+ * provider by the endpoint file's own `subscribe` export and taken down
17
+ * by its `unsubscribe` export, which the platform runs at publish and
18
+ * teardown.
18
19
  */
19
20
  integration: string;
21
+ /** The sentence the editor shows for what fires this endpoint, e.g. "When a task is completed in Asana". */
22
+ label?: string;
20
23
  paused?: boolean;
21
24
  } | {
22
25
  provider: "poll";
23
26
  /** `<n>m` (1–59) or `<n>h` (1–23) */
24
27
  every: string;
28
+ /** The sentence the editor shows for this poll, e.g. "Every 10 minutes, new Stripe charges". */
29
+ label?: string;
25
30
  paused?: boolean;
26
31
  };
32
+ /**
33
+ * What a poll-triggered run receives as `input.__poll`. Only on a fire from
34
+ * the poll's schedule; a request call to the same endpoint carries none.
35
+ */
36
+ export type ZitePollInput = {
37
+ /** What the previous successful run returned as `cursor`; null on the first run. */
38
+ cursor: string | null;
39
+ /** Ids earlier runs returned as `handled`, newest last. Skip these. */
40
+ seen: string[];
41
+ };
42
+ /**
43
+ * What a provider trigger's delivery carries as `input.__webhook`, beside the
44
+ * provider's own body. `cursor` and `seen` work as they do for a poll: for a
45
+ * provider whose notification only says that something changed, `subscribe`
46
+ * returns a starting `cursor` and each delivery returns the next one. A
47
+ * trigger that keeps a cursor gets one delivery at a time.
48
+ */
49
+ export type ZiteTriggerDelivery = {
50
+ headers: Record<string, string>;
51
+ /** The provider's id for this delivery; redeliveries share it. */
52
+ deliveryId?: string;
53
+ provider?: string;
54
+ /** What `subscribe` or the previous successful delivery returned as `cursor`. */
55
+ cursor: string | null;
56
+ /** Ids earlier deliveries returned as `handled`, newest last. Skip these. */
57
+ seen: string[];
58
+ };
59
+ /**
60
+ * What a poll-triggered run, or a provider trigger's delivery, returns for
61
+ * the platform to keep: its new `cursor`, and the id of every item it acted
62
+ * on, so the next run's `seen` includes them even if the cursor hands them
63
+ * back.
64
+ */
65
+ export type ZitePollResult = {
66
+ cursor?: string;
67
+ handled?: string[];
68
+ };
27
69
  export interface ZiteRequestContext {
28
70
  user: {
29
71
  id: string;
@@ -116,7 +158,15 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
116
158
  */
117
159
  validatesInput?: boolean;
118
160
  execute: (params: {
119
- input: TInput;
161
+ input: TTrigger extends {
162
+ provider: "poll";
163
+ } ? TInput & {
164
+ __poll?: ZitePollInput;
165
+ } : TTrigger extends {
166
+ integration: string;
167
+ } ? TInput & {
168
+ __webhook?: ZiteTriggerDelivery;
169
+ } : TInput;
120
170
  context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;
121
171
  } & (TStream extends true ? {
122
172
  stream: ZiteStreamInterface;
@@ -131,6 +131,48 @@ describe("platform-triggered runs", () => {
131
131
  });
132
132
  await expect(endpoint.execute({ input: undefined, context: requestContext })).rejects.toMatchObject({ code: "BAD_REQUEST" });
133
133
  });
134
+ it("hands a poll its cursor and seen ids, typed", async () => {
135
+ const endpoint = createEndpoint({
136
+ inputSchema: z.object({}),
137
+ trigger: { provider: "poll", every: "5m" },
138
+ execute: async ({ input }) => {
139
+ const seen = new Set(input.__poll?.seen);
140
+ const items = ["a", "b"].filter((id) => !seen.has(id));
141
+ return { cursor: input.__poll?.cursor ?? "start", handled: items };
142
+ },
143
+ });
144
+ await expect(endpoint.execute({
145
+ input: { __poll: { cursor: "c1", seen: ["a"] } },
146
+ context: { user: null },
147
+ })).resolves.toEqual({ cursor: "c1", handled: ["b"] });
148
+ });
149
+ it("gives __poll only to poll triggers", () => {
150
+ createEndpoint({
151
+ inputSchema: z.object({ action: z.string() }),
152
+ trigger: { integration: "github" },
153
+ execute: async ({ input }) => {
154
+ // @ts-expect-error a provider trigger's input has no __poll
155
+ return input.__poll;
156
+ },
157
+ });
158
+ });
159
+ it("hands a provider trigger's delivery its cursor and seen ids, typed", async () => {
160
+ const endpoint = createEndpoint({
161
+ inputSchema: z.object({}),
162
+ trigger: { integration: "googleCalendar" },
163
+ execute: async ({ input }) => {
164
+ const seen = new Set(input.__webhook?.seen);
165
+ const changed = ["e1", "e2"].filter((id) => !seen.has(id));
166
+ return { cursor: `${input.__webhook?.cursor}+1`, handled: changed };
167
+ },
168
+ });
169
+ await expect(endpoint.execute({
170
+ input: {
171
+ __webhook: { headers: {}, cursor: "sync1", seen: ["e1"] },
172
+ },
173
+ context: { user: null },
174
+ })).resolves.toEqual({ cursor: "sync1+1", handled: ["e2"] });
175
+ });
134
176
  it("does not validate a webhook fire", async () => {
135
177
  const endpoint = createEndpoint({
136
178
  inputSchema: z.object({ token: z.string().min(10) }),
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/index.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/index.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -382,7 +382,7 @@ async function bundleEndpointsImpl(baseDir, root, endpointNames) {
382
382
  for (const name of endpointNames) {
383
383
  if (root === 'automations' && name !== AUTOMATION_ENDPOINT_NAME) {
384
384
  endpointErrors[name] =
385
- `An automation is one workflow, src/workflow.ts, bundled as "${AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
385
+ `An automation is one workflow, src/index.ts, bundled as "${AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
386
386
  continue;
387
387
  }
388
388
  const entryFile = endpointEntryFile(root, name);
package/dist/esm/cli.js CHANGED
@@ -40,7 +40,7 @@ async function main() {
40
40
  console.error(' dev Run sync then watch for changes (like npx convex dev)');
41
41
  console.error(' generate Regenerate every app and automation .zite/ (monorepo)');
42
42
  console.error(' check Run tsc --noEmit (and vite build where there is one) for every app and automation');
43
- console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/workflow.ts) for cloudflare-lambda');
43
+ console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/index.ts) for cloudflare-lambda');
44
44
  process.exit(1);
45
45
  }
46
46
  }
@@ -0,0 +1,14 @@
1
+ export type ZiteProjectUser = {
2
+ uuid: string;
3
+ firstName: string | null;
4
+ lastName: string | null;
5
+ email: string;
6
+ profilePictureUrl: string | null;
7
+ };
8
+ export type MetaListUsersResult = {
9
+ users: ZiteProjectUser[];
10
+ };
11
+ export declare class ZiteMeta {
12
+ static listUsers(): Promise<MetaListUsersResult>;
13
+ }
14
+ export declare const Meta: typeof ZiteMeta;
@@ -0,0 +1,8 @@
1
+ import { getSdkCall } from '../internal/sdkCall.js';
2
+ const META_SDK_INTEGRATION_ID = '__meta__';
3
+ export class ZiteMeta {
4
+ static listUsers() {
5
+ return getSdkCall()(META_SDK_INTEGRATION_ID, 'ZiteMeta', 'listUsers', {});
6
+ }
7
+ }
8
+ export const Meta = ZiteMeta;
@@ -0,0 +1,25 @@
1
+ export interface NotificationLink {
2
+ path?: string;
3
+ params?: Record<string, string>;
4
+ }
5
+ export type NotificationsCreateParams = {
6
+ recipients: string[];
7
+ title: string;
8
+ body?: string;
9
+ link?: NotificationLink;
10
+ path?: string;
11
+ params?: Record<string, string>;
12
+ payload?: Record<string, unknown>;
13
+ idempotencyKey?: string;
14
+ };
15
+ export type NotificationsCreateResult = {
16
+ created: number;
17
+ } | {
18
+ created: 0;
19
+ preview: true;
20
+ wouldCreate: number;
21
+ };
22
+ export declare class ZiteNotifications {
23
+ static create(params: NotificationsCreateParams): Promise<NotificationsCreateResult>;
24
+ }
25
+ export declare const Notifications: typeof ZiteNotifications;
@@ -0,0 +1,8 @@
1
+ import { getSdkCall } from '../internal/sdkCall.js';
2
+ const NOTIFICATIONS_SDK_INTEGRATION_ID = '__notifications__';
3
+ export class ZiteNotifications {
4
+ static create(params) {
5
+ return getSdkCall()(NOTIFICATIONS_SDK_INTEGRATION_ID, 'ZiteNotifications', 'create', params);
6
+ }
7
+ }
8
+ export const Notifications = ZiteNotifications;
@@ -18,12 +18,12 @@ export declare function listSourceDirs(cwd?: string): SourceDir[];
18
18
  /** The source dir with this bare name, under whichever root holds it. */
19
19
  export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
20
20
  /**
21
- * An automation is one workflow: its single endpoint is `src/workflow.ts` and
21
+ * An automation is one workflow: its single endpoint is `src/index.ts` and
22
22
  * its name is fixed, so nothing about an automation is keyed on a file name
23
23
  * the way an app's `src/api/<name>.ts` endpoints are.
24
24
  */
25
- export declare const AUTOMATION_ENDPOINT_NAME = "workflow";
26
- export declare const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
25
+ export declare const AUTOMATION_ENDPOINT_NAME = "index";
26
+ export declare const AUTOMATION_ENTRY_FILE = "src/index.ts";
27
27
  /** The root a source dir sits under, read from its parent directory. */
28
28
  export declare function sourceRootOf(sourceDirPath: string): SourceRoot | undefined;
29
29
  /** An endpoint's entry file, relative to the source dir. */
@@ -27,12 +27,12 @@ export function findSourceDir(name, cwd = ".") {
27
27
  return listSourceDirs(cwd).find((d) => d.dir === name);
28
28
  }
29
29
  /**
30
- * An automation is one workflow: its single endpoint is `src/workflow.ts` and
30
+ * An automation is one workflow: its single endpoint is `src/index.ts` and
31
31
  * its name is fixed, so nothing about an automation is keyed on a file name
32
32
  * the way an app's `src/api/<name>.ts` endpoints are.
33
33
  */
34
- export const AUTOMATION_ENDPOINT_NAME = "workflow";
35
- export const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
34
+ export const AUTOMATION_ENDPOINT_NAME = "index";
35
+ export const AUTOMATION_ENTRY_FILE = "src/index.ts";
36
36
  /** The root a source dir sits under, read from its parent directory. */
37
37
  export function sourceRootOf(sourceDirPath) {
38
38
  const parent = basename(dirname(resolve(sourceDirPath)));
@@ -43,13 +43,13 @@ export function sourceRootOf(sourceDirPath) {
43
43
  /** An endpoint's entry file, relative to the source dir. */
44
44
  export function endpointEntryFile(root, name) {
45
45
  return root === "automations"
46
- ? AUTOMATION_WORKFLOW_FILE
46
+ ? AUTOMATION_ENTRY_FILE
47
47
  : join("src", "api", `${name}.ts`);
48
48
  }
49
49
  /** Every endpoint name a source dir has on disk. */
50
50
  export function listEndpointNames(root, sourceDirPath) {
51
51
  if (root === "automations") {
52
- return existsSync(join(sourceDirPath, AUTOMATION_WORKFLOW_FILE))
52
+ return existsSync(join(sourceDirPath, AUTOMATION_ENTRY_FILE))
53
53
  ? [AUTOMATION_ENDPOINT_NAME]
54
54
  : [];
55
55
  }
@@ -9,9 +9,9 @@ describe("source roots", () => {
9
9
  expect(sourceRootOf("/repo/automations/digest")).toBe("automations");
10
10
  expect(sourceRootOf("/repo/packages/ui")).toBeUndefined();
11
11
  });
12
- it("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/workflow.ts", () => {
12
+ it("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/index.ts", () => {
13
13
  expect(endpointEntryFile("apps", "listItems")).toBe("src/api/listItems.ts");
14
- expect(endpointEntryFile("automations", "workflow")).toBe("src/workflow.ts");
14
+ expect(endpointEntryFile("automations", "index")).toBe("src/index.ts");
15
15
  });
16
16
  it("lists what is on disk under each layout", () => {
17
17
  const repo = mkdtempSync(join(tmpdir(), "zitejs-roots-"));
@@ -20,9 +20,9 @@ describe("source roots", () => {
20
20
  writeFileSync(join(repo, "apps/crm/src/api/apiTypes.d.ts"), "");
21
21
  mkdirSync(join(repo, "automations/digest/src"), { recursive: true });
22
22
  mkdirSync(join(repo, "automations/empty/src"), { recursive: true });
23
- writeFileSync(join(repo, "automations/digest/src/workflow.ts"), "");
23
+ writeFileSync(join(repo, "automations/digest/src/index.ts"), "");
24
24
  expect(listEndpointNames("apps", join(repo, "apps/crm"))).toEqual(["listItems"]);
25
- expect(listEndpointNames("automations", join(repo, "automations/digest"))).toEqual(["workflow"]);
25
+ expect(listEndpointNames("automations", join(repo, "automations/digest"))).toEqual(["index"]);
26
26
  expect(listEndpointNames("automations", join(repo, "automations/empty"))).toEqual([]);
27
27
  });
28
28
  });
@@ -1411,7 +1411,7 @@ export function generateIntegrationsIndex(entries, opts) {
1411
1411
  "// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.",
1412
1412
  "// zitejs/integrations — an integration's credentials by its zite.config.json entry name.",
1413
1413
  "//",
1414
- "// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):",
1414
+ "// Usage in endpoints (src/api/*.ts) and automations (src/index.ts):",
1415
1415
  "// import { integrations } from 'zitejs/integrations';",
1416
1416
  "//",
1417
1417
  "// const crm = await integrations.get('<entry name>');",
@@ -460,7 +460,7 @@ describe('generateIntegrationsIndex', () => {
460
460
  "// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.
461
461
  // zitejs/integrations — an integration's credentials by its zite.config.json entry name.
462
462
  //
463
- // Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):
463
+ // Usage in endpoints (src/api/*.ts) and automations (src/index.ts):
464
464
  // import { integrations } from 'zitejs/integrations';
465
465
  //
466
466
  // const crm = await integrations.get('<entry name>');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.125",
3
+ "version": "0.9.126",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "sideEffects": false,