zitejs 0.9.113 → 0.9.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCaller = void 0;
4
+ var index_js_1 = require("../caller/index.js");
5
+ Object.defineProperty(exports, "createCaller", { enumerable: true, get: function () { return index_js_1.createCaller; } });
@@ -82,6 +82,12 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
82
82
  * `{ user: null }` for a webhook fire exactly as it does for a cron one.
83
83
  */
84
84
  webhook?: TWebhook;
85
+ /**
86
+ * Set by {@link createEndpoint}, not by hand. The worker validates
87
+ * `inputSchema` for bundles built before this did, and skips ones that
88
+ * declare it — parsing twice would apply a schema's transforms twice.
89
+ */
90
+ validatesInput?: boolean;
85
91
  execute: (params: {
86
92
  input: TInput;
87
93
  context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;
@@ -89,4 +95,11 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
89
95
  stream: ZiteStreamInterface;
90
96
  } : {})) => Promise<TOutput> | TOutput;
91
97
  }
98
+ /**
99
+ * Create an endpoint definition, parsing `input` against `inputSchema` before
100
+ * `execute` runs. Nothing downstream parses it — the worker calls `execute` on
101
+ * whatever this returns — so while this was a bare `return config` a required
102
+ * field could be missing and the endpoint ran anyway. Parsing here is also what
103
+ * makes `TRawInput` -> `TInput` (defaults, transforms) true at runtime.
104
+ */
92
105
  export declare function createEndpoint<TInput = unknown, TOutput = unknown, TStream extends boolean = false, TSchedule extends ZiteSchedule | undefined = undefined, TWebhook extends ZiteWebhook | undefined = undefined, TRawInput = TInput>(config: EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput>): EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput>;
@@ -31,6 +31,68 @@ class ZiteError extends Error {
31
31
  }
32
32
  }
33
33
  exports.ZiteError = ZiteError;
34
+ /** Injected by the runner on a platform-fired run — see `isPlatformTriggeredInput`. */
35
+ const PLATFORM_TRIGGER_KEYS = ["__cron", "__webhook"];
36
+ function isPlatformTriggeredInput(input) {
37
+ if (typeof input !== "object" || input === null)
38
+ return false;
39
+ return PLATFORM_TRIGGER_KEYS.some((key) => Object.prototype.hasOwnProperty.call(input, key));
40
+ }
41
+ function isIssueBearingError(error) {
42
+ return (typeof error === "object" &&
43
+ error !== null &&
44
+ Array.isArray(error.issues));
45
+ }
46
+ function describeIssues(error) {
47
+ return error.issues
48
+ .map((issue) => {
49
+ const path = issue.path.map((part) => String(part)).join(".");
50
+ return path ? `${path}: ${issue.message}` : issue.message;
51
+ })
52
+ .join("; ");
53
+ }
54
+ /**
55
+ * Create an endpoint definition, parsing `input` against `inputSchema` before
56
+ * `execute` runs. Nothing downstream parses it — the worker calls `execute` on
57
+ * whatever this returns — so while this was a bare `return config` a required
58
+ * field could be missing and the endpoint ran anyway. Parsing here is also what
59
+ * makes `TRawInput` -> `TInput` (defaults, transforms) true at runtime.
60
+ */
34
61
  function createEndpoint(config) {
35
- return config;
62
+ const { inputSchema, execute } = config;
63
+ const firesWithoutARequest = Boolean(config.schedule || config.webhook);
64
+ // An `inputSchema` that isn't a validator stays inert, as it was.
65
+ if (!inputSchema || typeof inputSchema.parse !== "function")
66
+ return config;
67
+ return {
68
+ ...config,
69
+ validatesInput: true,
70
+ // `async` so a schema failure rejects: `execute` may be declared sync, and
71
+ // a sync throw would escape the caller's `.catch()`.
72
+ execute: async (params) => {
73
+ // Cron and webhook fires carry no request body, so a schema written for
74
+ // request callers doesn't describe them.
75
+ if (isPlatformTriggeredInput(params.input))
76
+ return execute(params);
77
+ // Same, for a fire with no input object to hold the sentinel: the runner
78
+ // only merges `__cron` into a plain object.
79
+ if (firesWithoutARequest && params.input === undefined) {
80
+ return execute(params);
81
+ }
82
+ let parsed;
83
+ try {
84
+ parsed = inputSchema.parse(params.input);
85
+ }
86
+ catch (error) {
87
+ throw new ZiteError({
88
+ code: "BAD_REQUEST",
89
+ message: isIssueBearingError(error)
90
+ ? `Invalid input: ${describeIssues(error)}`
91
+ : `Invalid input: ${String(error?.message ?? error)}`,
92
+ userFacingMessage: "Some of the information sent with this request was missing or invalid.",
93
+ });
94
+ }
95
+ return execute({ ...params, input: parsed });
96
+ },
97
+ };
36
98
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const zod_1 = require("zod");
5
+ const index_js_1 = require("./index.js");
6
+ const index_js_2 = require("../runtime/index.js");
7
+ const requestContext = { user: { id: "u1", email: "a@b.c" } };
8
+ (0, vitest_1.afterEach)(() => {
9
+ vitest_1.vi.unstubAllGlobals();
10
+ });
11
+ (0, vitest_1.describe)("createEndpoint input validation", () => {
12
+ (0, vitest_1.it)("rejects input that does not satisfy inputSchema, without running execute", async () => {
13
+ const execute = vitest_1.vi.fn();
14
+ const endpoint = (0, index_js_1.createEndpoint)({
15
+ inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
16
+ execute,
17
+ });
18
+ await (0, vitest_1.expect)(endpoint.execute({ input: {}, context: requestContext })).rejects.toBeInstanceOf(index_js_1.ZiteError);
19
+ (0, vitest_1.expect)(execute).not.toHaveBeenCalled();
20
+ });
21
+ (0, vitest_1.it)("surfaces the failure as BAD_REQUEST and names the offending field", async () => {
22
+ const endpoint = (0, index_js_1.createEndpoint)({
23
+ inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
24
+ execute: async () => "unreachable",
25
+ });
26
+ await (0, vitest_1.expect)(endpoint.execute({ input: {}, context: requestContext })).rejects.toMatchObject({
27
+ code: "BAD_REQUEST",
28
+ message: vitest_1.expect.stringContaining("token"),
29
+ });
30
+ });
31
+ (0, vitest_1.it)("rejects a call with no input at all", async () => {
32
+ const execute = vitest_1.vi.fn();
33
+ const endpoint = (0, index_js_1.createEndpoint)({
34
+ inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
35
+ execute,
36
+ });
37
+ await (0, vitest_1.expect)(endpoint.execute({
38
+ input: undefined,
39
+ context: requestContext,
40
+ })).rejects.toMatchObject({ code: "BAD_REQUEST" });
41
+ (0, vitest_1.expect)(execute).not.toHaveBeenCalled();
42
+ });
43
+ (0, vitest_1.it)("hands execute the parsed value, so schema defaults finally apply", async () => {
44
+ const endpoint = (0, index_js_1.createEndpoint)({
45
+ inputSchema: zod_1.z.object({
46
+ token: zod_1.z.string(),
47
+ limit: zod_1.z.number().default(10),
48
+ }),
49
+ execute: async ({ input }) => input,
50
+ });
51
+ await (0, vitest_1.expect)(endpoint.execute({
52
+ input: { token: "abc" },
53
+ context: requestContext,
54
+ })).resolves.toEqual({ token: "abc", limit: 10 });
55
+ });
56
+ (0, vitest_1.it)("passes valid input through untouched", async () => {
57
+ const endpoint = (0, index_js_1.createEndpoint)({
58
+ inputSchema: zod_1.z.object({
59
+ token: zod_1.z.string().min(10),
60
+ action: zod_1.z.enum(["load", "accept"]),
61
+ }),
62
+ execute: async ({ input }) => input.action,
63
+ });
64
+ await (0, vitest_1.expect)(endpoint.execute({
65
+ input: { token: "b69779ae-eeea", action: "load" },
66
+ context: requestContext,
67
+ })).resolves.toBe("load");
68
+ });
69
+ (0, vitest_1.it)("declares that it validates, so the worker does not parse a second time", () => {
70
+ const withSchema = (0, index_js_1.createEndpoint)({
71
+ inputSchema: zod_1.z.object({ token: zod_1.z.string() }),
72
+ execute: async () => null,
73
+ });
74
+ const withoutSchema = (0, index_js_1.createEndpoint)({ execute: async () => null });
75
+ (0, vitest_1.expect)(withSchema.validatesInput).toBe(true);
76
+ (0, vitest_1.expect)(withoutSchema.validatesInput).toBeUndefined();
77
+ });
78
+ (0, vitest_1.it)("leaves an endpoint without an inputSchema exactly as it was", async () => {
79
+ const endpoint = (0, index_js_1.createEndpoint)({
80
+ execute: async ({ input }) => input,
81
+ });
82
+ await (0, vitest_1.expect)(endpoint.execute({
83
+ input: { anything: true },
84
+ context: requestContext,
85
+ })).resolves.toEqual({ anything: true });
86
+ });
87
+ (0, vitest_1.it)("preserves the stream handle for streaming endpoints", async () => {
88
+ const stream = { write: async () => { }, forward: async () => "" };
89
+ const endpoint = (0, index_js_1.createEndpoint)({
90
+ stream: true,
91
+ inputSchema: zod_1.z.object({ prompt: zod_1.z.string() }),
92
+ execute: async (params) => params.stream === stream,
93
+ });
94
+ await (0, vitest_1.expect)(endpoint.execute({
95
+ input: { prompt: "hi" },
96
+ context: requestContext,
97
+ stream,
98
+ })).resolves.toBe(true);
99
+ });
100
+ });
101
+ (0, vitest_1.describe)("platform-triggered runs", () => {
102
+ (0, vitest_1.it)("does not validate a cron fire, and leaves __cron readable", async () => {
103
+ const endpoint = (0, index_js_1.createEndpoint)({
104
+ inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
105
+ schedule: {
106
+ scheduleType: "recurring",
107
+ schedule: { frequency: "hourly", interval: 6 },
108
+ timezone: "UTC",
109
+ },
110
+ execute: async ({ input }) => input,
111
+ });
112
+ await (0, vitest_1.expect)(endpoint.execute({
113
+ input: { __cron: { scheduleId: "s1" } },
114
+ context: { user: null },
115
+ })).resolves.toEqual({ __cron: { scheduleId: "s1" } });
116
+ });
117
+ (0, vitest_1.it)("does not validate a scheduled fire that carries no input object", async () => {
118
+ const endpoint = (0, index_js_1.createEndpoint)({
119
+ inputSchema: zod_1.z.string(),
120
+ schedule: {
121
+ scheduleType: "recurring",
122
+ schedule: { frequency: "hourly", interval: 6 },
123
+ timezone: "UTC",
124
+ },
125
+ execute: async ({ input }) => input ?? "(no input)",
126
+ });
127
+ await (0, vitest_1.expect)(endpoint.execute({ input: undefined, context: { user: null } })).resolves.toBe("(no input)");
128
+ });
129
+ (0, vitest_1.it)("still validates an ordinary endpoint called with no input", async () => {
130
+ const endpoint = (0, index_js_1.createEndpoint)({
131
+ inputSchema: zod_1.z.string(),
132
+ execute: async ({ input }) => input,
133
+ });
134
+ await (0, vitest_1.expect)(endpoint.execute({ input: undefined, context: requestContext })).rejects.toMatchObject({ code: "BAD_REQUEST" });
135
+ });
136
+ (0, vitest_1.it)("does not validate a webhook fire", async () => {
137
+ const endpoint = (0, index_js_1.createEndpoint)({
138
+ inputSchema: zod_1.z.object({ token: zod_1.z.string().min(10) }),
139
+ webhook: {},
140
+ execute: async ({ input }) => input,
141
+ });
142
+ await (0, vitest_1.expect)(endpoint.execute({
143
+ input: { __webhook: { headers: {} }, id: 7 },
144
+ context: { user: null },
145
+ })).resolves.toEqual({ __webhook: { headers: {} }, id: 7 });
146
+ });
147
+ });
148
+ /**
149
+ * The failure this was written for: an endpoint that looks a customer up by a
150
+ * token, called with no token. The undefined was dropped on the way to the
151
+ * database and the unfiltered lookup returned the first row of the table.
152
+ * Either guard alone stops it; they fail at different layers, so both are here.
153
+ */
154
+ (0, vitest_1.describe)("regression: token lookup on an endpoint called without a token", () => {
155
+ function buildEndpoint(onMatch) {
156
+ const clients = (0, index_js_2.createTableClient)("Clients");
157
+ return (0, index_js_1.createEndpoint)({
158
+ inputSchema: zod_1.z.object({
159
+ token: zod_1.z.string().min(10),
160
+ action: zod_1.z.enum(["load", "accept"]),
161
+ }),
162
+ execute: async ({ input }) => {
163
+ const row = await clients.findOne({ filters: { token: input.token } });
164
+ if (!row)
165
+ return "invalid_token";
166
+ onMatch(row.nom);
167
+ return "ok";
168
+ },
169
+ });
170
+ }
171
+ (0, vitest_1.it)("stops at the schema, so the database is never reached", async () => {
172
+ const dispatched = [];
173
+ vitest_1.vi.stubGlobal("__wrapSdkCall", async (_i, _c, m) => {
174
+ dispatched.push(m);
175
+ return { id: "1", token: "", nom: "an unrelated customer" };
176
+ });
177
+ const matched = [];
178
+ await (0, vitest_1.expect)(buildEndpoint((nom) => matched.push(nom)).execute({
179
+ input: {},
180
+ context: requestContext,
181
+ })).rejects.toMatchObject({ code: "BAD_REQUEST" });
182
+ (0, vitest_1.expect)(dispatched).toEqual([]);
183
+ (0, vitest_1.expect)(matched).toEqual([]);
184
+ });
185
+ (0, vitest_1.it)("stops at the query too, if an undefined token reaches it another way", async () => {
186
+ const dispatched = [];
187
+ vitest_1.vi.stubGlobal("__wrapSdkCall", async (_i, _c, m) => {
188
+ dispatched.push(m);
189
+ return { id: "1", token: "", nom: "an unrelated customer" };
190
+ });
191
+ const warn = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
192
+ const clients = (0, index_js_2.createTableClient)("Clients");
193
+ const token = undefined;
194
+ // Answers "no match" rather than throwing: `if (!row) return
195
+ // { status: 'invalid_token' }` is what the endpoint already does, so this
196
+ // makes the incident path correct without the app changing anything.
197
+ const row = await clients.findOne({ filters: { token } });
198
+ (0, vitest_1.expect)(row).toBeUndefined();
199
+ (0, vitest_1.expect)(dispatched).toEqual([]);
200
+ (0, vitest_1.expect)(warn).toHaveBeenCalled();
201
+ warn.mockRestore();
202
+ });
203
+ });
@@ -1 +1,25 @@
1
+ /**
2
+ * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
+ *
4
+ * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
+ * Reads endpoints from src/api/*.ts, resolves SDK imports via .zite/backend.ts
6
+ * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
+ * code suitable for the cloudflare-lambda worker runtime.
8
+ *
9
+ * Usage:
10
+ * npx zitejs bundle # bundle all endpoints
11
+ * npx zitejs bundle --app admin-panel # bundle endpoints for a specific app
12
+ * npx zitejs bundle --script <path> # bundle a one-off script
13
+ *
14
+ * Output: JSON to stdout
15
+ * { bundledEndpoints: Record<string, string>, endpointErrors?: Record<string, string> }
16
+ * or for --script: { bundledCode: string } or { error: string }
17
+ *
18
+ * Dependencies (add to zitejs package.json):
19
+ * "esbuild": "^0.25.0"
20
+ * "@babel/parser": "^7.26.0"
21
+ */
22
+ import * as esbuild from 'esbuild';
23
+ export declare const UNSUPPORTED_IN_WORKERD: Set<string>;
24
+ export declare const BASE_BUILD_OPTIONS: esbuild.BuildOptions;
1
25
  export declare function runBundle(): Promise<void>;
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BASE_BUILD_OPTIONS = exports.UNSUPPORTED_IN_WORKERD = void 0;
36
37
  exports.runBundle = runBundle;
37
38
  /**
38
39
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
@@ -59,14 +60,21 @@ const esbuild = __importStar(require("esbuild"));
59
60
  const path = __importStar(require("path"));
60
61
  const fs = __importStar(require("fs"));
61
62
  const parser_1 = require("@babel/parser");
62
- const _NODE_BUILTIN_NAMES = [
63
- 'http', 'https', 'http2', 'stream', 'buffer', 'util', 'events', 'crypto',
64
- 'path', 'fs', 'url', 'querystring', 'zlib', 'net', 'tls', 'os', 'assert',
65
- 'process', 'child_process', 'cluster', 'dgram', 'dns', 'inspector', 'module',
66
- 'perf_hooks', 'readline', 'repl', 'string_decoder', 'timers', 'tty', 'v8',
67
- 'vm', 'worker_threads', 'async_hooks', 'trace_events', 'punycode',
68
- ];
69
- const NODE_BUILTINS = _NODE_BUILTIN_NAMES.flatMap(m => [m, `node:${m}`]);
63
+ const node_module_1 = require("node:module");
64
+ // workerd's `nodejs_compat` does not provide these. Left external they bundle
65
+ // fine and then kill the worker at load time with `No such module`; excluded,
66
+ // esbuild fails the one endpoint with a resolvable error instead. Pinned by
67
+ // apps/cloudflare-lambda tests/sdk-compat/node-builtin-support.test.ts.
68
+ exports.UNSUPPORTED_IN_WORKERD = new Set([
69
+ 'child_process', 'dgram', 'inspector', 'inspector/promises', 'perf_hooks',
70
+ 'readline', 'readline/promises', 'repl', 'tty', 'v8', 'worker_threads',
71
+ ]);
72
+ // External, not bundled: the worker supplies them. Sourced from Node because
73
+ // the hand-written list covered 36 of 68, so a dependency reaching for a name
74
+ // we forgot (`constants`, `fs/promises`) failed the whole endpoint.
75
+ const NODE_BUILTINS = node_module_1.builtinModules
76
+ .filter(m => !m.startsWith('_') && !exports.UNSUPPORTED_IN_WORKERD.has(m))
77
+ .flatMap(m => [m, `node:${m}`]);
70
78
  const PREBUNDLED_LIBS = {
71
79
  '@zite/endpoints-runtime-sdk': '__zite-runtime__.js',
72
80
  'zod': '__zod__.js',
@@ -87,7 +95,7 @@ const PREBUNDLED_LIBS = {
87
95
  '@google/generative-ai': '__gemini__.js',
88
96
  '@elevenlabs/elevenlabs-js': '__elevenlabs__.js',
89
97
  };
90
- const BASE_BUILD_OPTIONS = {
98
+ exports.BASE_BUILD_OPTIONS = {
91
99
  bundle: true,
92
100
  write: false,
93
101
  format: 'esm',
@@ -392,7 +400,7 @@ async function bundleEndpointsImpl(baseDir, endpointNames) {
392
400
  const wrapperCode = generateEndpointWrapper(name, usedImports, sdkExportKinds, sdkSource);
393
401
  try {
394
402
  const result = await esbuild.build({
395
- ...BASE_BUILD_OPTIONS,
403
+ ...exports.BASE_BUILD_OPTIONS,
396
404
  stdin: {
397
405
  contents: wrapperCode,
398
406
  resolveDir: `${baseDir}/src`,
@@ -462,7 +470,7 @@ globalThis.__endpoint = endpoint;
462
470
  `;
463
471
  try {
464
472
  const result = await esbuild.build({
465
- ...BASE_BUILD_OPTIONS,
473
+ ...exports.BASE_BUILD_OPTIONS,
466
474
  stdin: {
467
475
  contents: dispatcherCode,
468
476
  resolveDir: `${baseDir}/src`,
@@ -542,7 +550,7 @@ ${bodySection}
542
550
  }`;
543
551
  try {
544
552
  const result = await esbuild.build({
545
- ...BASE_BUILD_OPTIONS,
553
+ ...exports.BASE_BUILD_OPTIONS,
546
554
  stdin: {
547
555
  contents: wrappedScript,
548
556
  loader: 'ts',
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTableClient = void 0;
4
+ var index_js_1 = require("../runtime/index.js");
5
+ Object.defineProperty(exports, "createTableClient", { enumerable: true, get: function () { return index_js_1.createTableClient; } });
@@ -8,6 +8,78 @@ exports.createAirtableClient = createAirtableClient;
8
8
  exports.createEmailClient = createEmailClient;
9
9
  const sdkCall_js_1 = require("../internal/sdkCall.js");
10
10
  const DB_INTEGRATION_ID = "databases";
11
+ function isPlainObject(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ /**
15
+ * Not thrown — the miss is the answer. Logged so the author can still find out
16
+ * their value was missing, since a silent null looks exactly like a legitimate
17
+ * no-match and would otherwise hide the bug that caused it.
18
+ */
19
+ function warnEmptySelector(className, method, detail) {
20
+ console.warn(`${className}.${method}: ${detail} is undefined, so this query would have ` +
21
+ "run unnarrowed and returned an arbitrary record. Returning no match " +
22
+ "instead. Check the value before querying, or pass null to match " +
23
+ "records where the field is empty.");
24
+ }
25
+ /**
26
+ * Whether a selector was written with nothing in it.
27
+ *
28
+ * `JSON.stringify` drops an undefined value, so `{ filters: { token: undefined } }`
29
+ * reaches base-runner as `{ filters: {} }` — a valid request for no filter.
30
+ * `findOne` answers that with the first row of the table, and the caller reads
31
+ * it back as a match.
32
+ *
33
+ * `findOne` treats it as "matched nothing" instead, which is what asking for
34
+ * `token === undefined` should mean. Callers already handle the miss —
35
+ * `if (!row) return { status: 'invalid_token' }` — so the shape that caused the
36
+ * incident becomes correct on its own, with no app change and nothing to break.
37
+ *
38
+ * **`findOne` only.** On `findAll`, "no filter" is a coherent request, and
39
+ * `filters: { status: maybeUndefined }` meaning "don't narrow on status" is an
40
+ * idiom that type-checks today (`RecordFilters` is a `?:` map, and apps build
41
+ * without `exactOptionalPropertyTypes`). It is also what Prisma does with
42
+ * `undefined` in a `where`. Only `findOne` has no sane reading, because an
43
+ * unnarrowed single-row query returns an arbitrary record rather than everything.
44
+ *
45
+ * A key the caller never wrote is untouched: `findOne({ filters: {} })` still
46
+ * means "no filter" and still returns the first row. Only a key written with
47
+ * nothing in it counts. (Unknown filter keys have the same consequence — see
48
+ * `RecordFilters` — but those are `keyof T` and caught at build time.)
49
+ */
50
+ function selectorMatchesNothing(className, method, params) {
51
+ if (!params)
52
+ return false;
53
+ const { filters } = params;
54
+ if (isPlainObject(filters)) {
55
+ for (const [key, value] of Object.entries(filters)) {
56
+ if (value === undefined) {
57
+ warnEmptySelector(className, method, `filter "${key}"`);
58
+ return true;
59
+ }
60
+ // `{ field: { contains: undefined } }` collapses to `{ field: {} }` — no
61
+ // condition on that field at all.
62
+ if (isPlainObject(value)) {
63
+ for (const [operator, operand] of Object.entries(value)) {
64
+ if (operand === undefined) {
65
+ warnEmptySelector(className, method, `filter "${key}.${operator}"`);
66
+ return true;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ // An undefined `id` leaves the query with no selector at all. Only counts
73
+ // when nothing else narrows it, so `findOne({ id: maybeId, filters: {...} })`
74
+ // still runs on the filters.
75
+ const wroteId = Object.prototype.hasOwnProperty.call(params, "id");
76
+ const hasFilters = isPlainObject(filters) && Object.keys(filters).length > 0;
77
+ if (wroteId && params.id === undefined && !hasFilters) {
78
+ warnEmptySelector(className, method, "`id`");
79
+ return true;
80
+ }
81
+ return false;
82
+ }
11
83
  function getBaseId() {
12
84
  try {
13
85
  return (globalThis.__ZITE_EXECUTION_CONFIG__?.baseId ??
@@ -34,16 +106,22 @@ function resolveTableId(className) {
34
106
  }
35
107
  function createTableClient(className) {
36
108
  return {
37
- findAll: (params) => (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, className, "findAll", {
38
- baseId: getBaseId(),
39
- tableId: resolveTableId(className),
40
- ...params,
41
- }),
42
- findOne: (params) => (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, className, "findOne", {
43
- baseId: getBaseId(),
44
- tableId: resolveTableId(className),
45
- ...params,
46
- }),
109
+ findAll: async (params) => {
110
+ return (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, className, "findAll", {
111
+ baseId: getBaseId(),
112
+ tableId: resolveTableId(className),
113
+ ...params,
114
+ });
115
+ },
116
+ findOne: async (params) => {
117
+ if (selectorMatchesNothing(className, "findOne", params))
118
+ return undefined;
119
+ return (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, className, "findOne", {
120
+ baseId: getBaseId(),
121
+ tableId: resolveTableId(className),
122
+ ...params,
123
+ });
124
+ },
47
125
  create: (params) => (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, className, "create", {
48
126
  baseId: getBaseId(),
49
127
  tableId: resolveTableId(className),
@@ -74,10 +152,12 @@ function createSqlClient() {
74
152
  }
75
153
  function createAuthClient() {
76
154
  return {
77
- findAllUsers: (options) => (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, "Auth", "findAllUsers", {
78
- baseId: getBaseId(),
79
- ...options,
80
- }),
155
+ findAllUsers: async (options) => {
156
+ return (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, "Auth", "findAllUsers", {
157
+ baseId: getBaseId(),
158
+ ...options,
159
+ });
160
+ },
81
161
  updateUserProfile: (userId, profile) => (0, sdkCall_js_1.getSdkCall)()(DB_INTEGRATION_ID, "Auth", "updateUserProfile", {
82
162
  baseId: getBaseId(),
83
163
  userId,
@@ -87,14 +167,20 @@ function createAuthClient() {
87
167
  }
88
168
  function createAirtableClient(integrationId, className, implicitParams) {
89
169
  return {
90
- findAll: (params) => (0, sdkCall_js_1.getSdkCall)()(integrationId, className, "findAll", {
91
- ...implicitParams,
92
- ...params,
93
- }),
94
- findOne: (params) => (0, sdkCall_js_1.getSdkCall)()(integrationId, className, "findOne", {
95
- ...implicitParams,
96
- ...params,
97
- }),
170
+ findAll: async (params) => {
171
+ return (0, sdkCall_js_1.getSdkCall)()(integrationId, className, "findAll", {
172
+ ...implicitParams,
173
+ ...params,
174
+ });
175
+ },
176
+ findOne: async (params) => {
177
+ if (selectorMatchesNothing(className, "findOne", params))
178
+ return undefined;
179
+ return (0, sdkCall_js_1.getSdkCall)()(integrationId, className, "findOne", {
180
+ ...implicitParams,
181
+ ...params,
182
+ });
183
+ },
98
184
  create: (params) => (0, sdkCall_js_1.getSdkCall)()(integrationId, className, "create", {
99
185
  ...implicitParams,
100
186
  ...params,
@@ -0,0 +1 @@
1
+ export {};