zitejs 0.9.114 → 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.
@@ -50,7 +50,8 @@ function toCamelCase(name) {
50
50
  }
51
51
  /**
52
52
  * camelCase for a FRESH sdkName, reading acronym runs as words: "VIP" ->
53
- * "vip", "APIKey" -> "apiKey" (plain toCamelCase yields "vIP" / "aPIKey").
53
+ * "vip", "APIKey" -> "apiKey" (plain toCamelCase yields "vIP" / "aPIKey"), and
54
+ * folding a diacritic into its base letter: "Filières" -> "filieres".
54
55
  *
55
56
  * Deliberately not folded into toCamelCase: that also derives endpoint
56
57
  * identifiers from existing filenames on every generate, where normalizing
@@ -58,7 +59,22 @@ function toCamelCase(name) {
58
59
  * chosen for the first time — generateSchema preserves existing sdkNames.
59
60
  */
60
61
  function toSdkName(name) {
62
+ // Decompose, then drop the combining marks, so an accented letter keeps its
63
+ // base rather than being read as punctuation. `toPascalCase` treats anything
64
+ // outside [a-zA-Z0-9] as a word separator and uppercases what follows it, so
65
+ // without this a table called "Fiches Filières" allocates `fichesFiliRes`.
66
+ //
67
+ // The bar is not "looks nicer" — it is Zite 1.0, which sanitised names this
68
+ // exact way (`getIntegrationNameMapping`) and therefore allocated
69
+ // `fichesFilieres`. Every app carried over from 1.0 compiles against that
70
+ // name, so a generator that disagrees hands a non-English base an accessor
71
+ // nobody would write and nothing resolves to.
72
+ //
73
+ // Only Latin diacritics fold. A name with no ASCII letters at all (Cyrillic,
74
+ // CJK, emoji) still collapses to `_` here, exactly as it did in 1.0.
61
75
  const deAcronymed = name
76
+ .normalize("NFD")
77
+ .replace(/[\u0300-\u036f]/g, "")
62
78
  .replace(/([A-Z]+)([A-Z][a-z])/g, (_, run, next) => run.charAt(0) + run.slice(1).toLowerCase() + next)
63
79
  .replace(/([A-Z])([A-Z]+)/g, (_, first, rest) => first + rest.toLowerCase());
64
80
  return toCamelCase(deAcronymed);
@@ -235,3 +235,51 @@ const recordTypeKeys = (source, typeName) => {
235
235
  (0, vitest_1.expect)(schema.tables[0].name).toBe("Deals");
236
236
  });
237
237
  });
238
+ /**
239
+ * `toPascalCase` reads anything outside [a-zA-Z0-9] as a word separator and
240
+ * uppercases what follows, so an accent used to split the word it sat inside:
241
+ * "Filières" -> `filiRes`. Zite 1.0 stripped the combining mark instead, and
242
+ * every app carried over from it compiles against THAT name — so this is a
243
+ * disagreement with the apps in the wild, not a cosmetic preference.
244
+ */
245
+ (0, vitest_1.describe)("diacritics in a fresh sdkName", () => {
246
+ const nameFor = (tableName) => (0, lib_js_1.generateSchema)(database([{ id: "tbl_1", name: tableName, fields: [] }]))
247
+ .tables[0].sdkName;
248
+ vitest_1.it.each([
249
+ ["Fiches Filières", "fichesFilieres"],
250
+ ["Recherches Métiers", "recherchesMetiers"],
251
+ ["Profil Compétences", "profilCompetences"],
252
+ ["Café Menu", "cafeMenu"],
253
+ ["Señor Clients", "senorClients"],
254
+ // Real, from a migrated base: an all-caps name where the accent sits inside
255
+ // an acronym run, so the de-acronym pass has to see the folded letter.
256
+ ["XICOH TRÁMITES", "xicohTramites"],
257
+ ])("folds %s into %s", (display, sdkName) => {
258
+ (0, vitest_1.expect)(nameFor(display)).toBe(sdkName);
259
+ });
260
+ (0, vitest_1.it)("folds a field name the same way", () => {
261
+ const schema = (0, lib_js_1.generateSchema)(database([
262
+ {
263
+ id: "tbl_1",
264
+ name: "Trámites",
265
+ fields: [field("fld_1", "Horario de Atención")],
266
+ },
267
+ ]));
268
+ (0, vitest_1.expect)(schema.tables[0].fields[0].sdkName).toBe("horarioDeAtencion");
269
+ });
270
+ (0, vitest_1.it)("leaves a name with no ASCII letters degenerate, as 1.0 did", () => {
271
+ // NFD only folds Latin diacritics. Nothing here to fold onto, so this
272
+ // collapses exactly as before — called out so the narrower scope is a
273
+ // decision on the record rather than an oversight.
274
+ (0, vitest_1.expect)(nameFor("Посещения")).toBe("_");
275
+ });
276
+ (0, vitest_1.it)("does not rename a table already locked to the old spelling", () => {
277
+ // The whole point of the lock: an app compiling against `fichesFiliRes`
278
+ // keeps it. Only names allocated from here on change.
279
+ const existingSchema = {
280
+ tables: [{ id: "tbl_1", sdkName: "fichesFiliRes", fields: [] }],
281
+ };
282
+ const schema = (0, lib_js_1.generateSchema)(database([{ id: "tbl_1", name: "Fiches Filières", fields: [] }]), existingSchema);
283
+ (0, vitest_1.expect)(schema.tables[0].sdkName).toBe("fichesFiliRes");
284
+ });
285
+ });
@@ -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>;
@@ -26,6 +26,68 @@ export class ZiteError extends Error {
26
26
  this.name = "ZiteError";
27
27
  }
28
28
  }
29
+ /** Injected by the runner on a platform-fired run — see `isPlatformTriggeredInput`. */
30
+ const PLATFORM_TRIGGER_KEYS = ["__cron", "__webhook"];
31
+ function isPlatformTriggeredInput(input) {
32
+ if (typeof input !== "object" || input === null)
33
+ return false;
34
+ return PLATFORM_TRIGGER_KEYS.some((key) => Object.prototype.hasOwnProperty.call(input, key));
35
+ }
36
+ function isIssueBearingError(error) {
37
+ return (typeof error === "object" &&
38
+ error !== null &&
39
+ Array.isArray(error.issues));
40
+ }
41
+ function describeIssues(error) {
42
+ return error.issues
43
+ .map((issue) => {
44
+ const path = issue.path.map((part) => String(part)).join(".");
45
+ return path ? `${path}: ${issue.message}` : issue.message;
46
+ })
47
+ .join("; ");
48
+ }
49
+ /**
50
+ * Create an endpoint definition, parsing `input` against `inputSchema` before
51
+ * `execute` runs. Nothing downstream parses it — the worker calls `execute` on
52
+ * whatever this returns — so while this was a bare `return config` a required
53
+ * field could be missing and the endpoint ran anyway. Parsing here is also what
54
+ * makes `TRawInput` -> `TInput` (defaults, transforms) true at runtime.
55
+ */
29
56
  export function createEndpoint(config) {
30
- return config;
57
+ const { inputSchema, execute } = config;
58
+ const firesWithoutARequest = Boolean(config.schedule || config.webhook);
59
+ // An `inputSchema` that isn't a validator stays inert, as it was.
60
+ if (!inputSchema || typeof inputSchema.parse !== "function")
61
+ return config;
62
+ return {
63
+ ...config,
64
+ validatesInput: true,
65
+ // `async` so a schema failure rejects: `execute` may be declared sync, and
66
+ // a sync throw would escape the caller's `.catch()`.
67
+ execute: async (params) => {
68
+ // Cron and webhook fires carry no request body, so a schema written for
69
+ // request callers doesn't describe them.
70
+ if (isPlatformTriggeredInput(params.input))
71
+ return execute(params);
72
+ // Same, for a fire with no input object to hold the sentinel: the runner
73
+ // only merges `__cron` into a plain object.
74
+ if (firesWithoutARequest && params.input === undefined) {
75
+ return execute(params);
76
+ }
77
+ let parsed;
78
+ try {
79
+ parsed = inputSchema.parse(params.input);
80
+ }
81
+ catch (error) {
82
+ throw new ZiteError({
83
+ code: "BAD_REQUEST",
84
+ message: isIssueBearingError(error)
85
+ ? `Invalid input: ${describeIssues(error)}`
86
+ : `Invalid input: ${String(error?.message ?? error)}`,
87
+ userFacingMessage: "Some of the information sent with this request was missing or invalid.",
88
+ });
89
+ }
90
+ return execute({ ...params, input: parsed });
91
+ },
92
+ };
31
93
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,201 @@
1
+ import { describe, it, expect, afterEach, vi } from "vitest";
2
+ import { z } from "zod";
3
+ import { createEndpoint, ZiteError } from "./index.js";
4
+ import { createTableClient } from "../runtime/index.js";
5
+ const requestContext = { user: { id: "u1", email: "a@b.c" } };
6
+ afterEach(() => {
7
+ vi.unstubAllGlobals();
8
+ });
9
+ describe("createEndpoint input validation", () => {
10
+ it("rejects input that does not satisfy inputSchema, without running execute", async () => {
11
+ const execute = vi.fn();
12
+ const endpoint = createEndpoint({
13
+ inputSchema: z.object({ token: z.string().min(10) }),
14
+ execute,
15
+ });
16
+ await expect(endpoint.execute({ input: {}, context: requestContext })).rejects.toBeInstanceOf(ZiteError);
17
+ expect(execute).not.toHaveBeenCalled();
18
+ });
19
+ it("surfaces the failure as BAD_REQUEST and names the offending field", async () => {
20
+ const endpoint = createEndpoint({
21
+ inputSchema: z.object({ token: z.string().min(10) }),
22
+ execute: async () => "unreachable",
23
+ });
24
+ await expect(endpoint.execute({ input: {}, context: requestContext })).rejects.toMatchObject({
25
+ code: "BAD_REQUEST",
26
+ message: expect.stringContaining("token"),
27
+ });
28
+ });
29
+ it("rejects a call with no input at all", async () => {
30
+ const execute = vi.fn();
31
+ const endpoint = createEndpoint({
32
+ inputSchema: z.object({ token: z.string().min(10) }),
33
+ execute,
34
+ });
35
+ await expect(endpoint.execute({
36
+ input: undefined,
37
+ context: requestContext,
38
+ })).rejects.toMatchObject({ code: "BAD_REQUEST" });
39
+ expect(execute).not.toHaveBeenCalled();
40
+ });
41
+ it("hands execute the parsed value, so schema defaults finally apply", async () => {
42
+ const endpoint = createEndpoint({
43
+ inputSchema: z.object({
44
+ token: z.string(),
45
+ limit: z.number().default(10),
46
+ }),
47
+ execute: async ({ input }) => input,
48
+ });
49
+ await expect(endpoint.execute({
50
+ input: { token: "abc" },
51
+ context: requestContext,
52
+ })).resolves.toEqual({ token: "abc", limit: 10 });
53
+ });
54
+ it("passes valid input through untouched", async () => {
55
+ const endpoint = createEndpoint({
56
+ inputSchema: z.object({
57
+ token: z.string().min(10),
58
+ action: z.enum(["load", "accept"]),
59
+ }),
60
+ execute: async ({ input }) => input.action,
61
+ });
62
+ await expect(endpoint.execute({
63
+ input: { token: "b69779ae-eeea", action: "load" },
64
+ context: requestContext,
65
+ })).resolves.toBe("load");
66
+ });
67
+ it("declares that it validates, so the worker does not parse a second time", () => {
68
+ const withSchema = createEndpoint({
69
+ inputSchema: z.object({ token: z.string() }),
70
+ execute: async () => null,
71
+ });
72
+ const withoutSchema = createEndpoint({ execute: async () => null });
73
+ expect(withSchema.validatesInput).toBe(true);
74
+ expect(withoutSchema.validatesInput).toBeUndefined();
75
+ });
76
+ it("leaves an endpoint without an inputSchema exactly as it was", async () => {
77
+ const endpoint = createEndpoint({
78
+ execute: async ({ input }) => input,
79
+ });
80
+ await expect(endpoint.execute({
81
+ input: { anything: true },
82
+ context: requestContext,
83
+ })).resolves.toEqual({ anything: true });
84
+ });
85
+ it("preserves the stream handle for streaming endpoints", async () => {
86
+ const stream = { write: async () => { }, forward: async () => "" };
87
+ const endpoint = createEndpoint({
88
+ stream: true,
89
+ inputSchema: z.object({ prompt: z.string() }),
90
+ execute: async (params) => params.stream === stream,
91
+ });
92
+ await expect(endpoint.execute({
93
+ input: { prompt: "hi" },
94
+ context: requestContext,
95
+ stream,
96
+ })).resolves.toBe(true);
97
+ });
98
+ });
99
+ describe("platform-triggered runs", () => {
100
+ it("does not validate a cron fire, and leaves __cron readable", async () => {
101
+ const endpoint = createEndpoint({
102
+ inputSchema: z.object({ token: z.string().min(10) }),
103
+ schedule: {
104
+ scheduleType: "recurring",
105
+ schedule: { frequency: "hourly", interval: 6 },
106
+ timezone: "UTC",
107
+ },
108
+ execute: async ({ input }) => input,
109
+ });
110
+ await expect(endpoint.execute({
111
+ input: { __cron: { scheduleId: "s1" } },
112
+ context: { user: null },
113
+ })).resolves.toEqual({ __cron: { scheduleId: "s1" } });
114
+ });
115
+ it("does not validate a scheduled fire that carries no input object", async () => {
116
+ const endpoint = createEndpoint({
117
+ inputSchema: z.string(),
118
+ schedule: {
119
+ scheduleType: "recurring",
120
+ schedule: { frequency: "hourly", interval: 6 },
121
+ timezone: "UTC",
122
+ },
123
+ execute: async ({ input }) => input ?? "(no input)",
124
+ });
125
+ await expect(endpoint.execute({ input: undefined, context: { user: null } })).resolves.toBe("(no input)");
126
+ });
127
+ it("still validates an ordinary endpoint called with no input", async () => {
128
+ const endpoint = createEndpoint({
129
+ inputSchema: z.string(),
130
+ execute: async ({ input }) => input,
131
+ });
132
+ await expect(endpoint.execute({ input: undefined, context: requestContext })).rejects.toMatchObject({ code: "BAD_REQUEST" });
133
+ });
134
+ it("does not validate a webhook fire", async () => {
135
+ const endpoint = createEndpoint({
136
+ inputSchema: z.object({ token: z.string().min(10) }),
137
+ webhook: {},
138
+ execute: async ({ input }) => input,
139
+ });
140
+ await expect(endpoint.execute({
141
+ input: { __webhook: { headers: {} }, id: 7 },
142
+ context: { user: null },
143
+ })).resolves.toEqual({ __webhook: { headers: {} }, id: 7 });
144
+ });
145
+ });
146
+ /**
147
+ * The failure this was written for: an endpoint that looks a customer up by a
148
+ * token, called with no token. The undefined was dropped on the way to the
149
+ * database and the unfiltered lookup returned the first row of the table.
150
+ * Either guard alone stops it; they fail at different layers, so both are here.
151
+ */
152
+ describe("regression: token lookup on an endpoint called without a token", () => {
153
+ function buildEndpoint(onMatch) {
154
+ const clients = createTableClient("Clients");
155
+ return createEndpoint({
156
+ inputSchema: z.object({
157
+ token: z.string().min(10),
158
+ action: z.enum(["load", "accept"]),
159
+ }),
160
+ execute: async ({ input }) => {
161
+ const row = await clients.findOne({ filters: { token: input.token } });
162
+ if (!row)
163
+ return "invalid_token";
164
+ onMatch(row.nom);
165
+ return "ok";
166
+ },
167
+ });
168
+ }
169
+ it("stops at the schema, so the database is never reached", async () => {
170
+ const dispatched = [];
171
+ vi.stubGlobal("__wrapSdkCall", async (_i, _c, m) => {
172
+ dispatched.push(m);
173
+ return { id: "1", token: "", nom: "an unrelated customer" };
174
+ });
175
+ const matched = [];
176
+ await expect(buildEndpoint((nom) => matched.push(nom)).execute({
177
+ input: {},
178
+ context: requestContext,
179
+ })).rejects.toMatchObject({ code: "BAD_REQUEST" });
180
+ expect(dispatched).toEqual([]);
181
+ expect(matched).toEqual([]);
182
+ });
183
+ it("stops at the query too, if an undefined token reaches it another way", async () => {
184
+ const dispatched = [];
185
+ vi.stubGlobal("__wrapSdkCall", async (_i, _c, m) => {
186
+ dispatched.push(m);
187
+ return { id: "1", token: "", nom: "an unrelated customer" };
188
+ });
189
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
190
+ const clients = createTableClient("Clients");
191
+ const token = undefined;
192
+ // Answers "no match" rather than throwing: `if (!row) return
193
+ // { status: 'invalid_token' }` is what the endpoint already does, so this
194
+ // makes the incident path correct without the app changing anything.
195
+ const row = await clients.findOne({ filters: { token } });
196
+ expect(row).toBeUndefined();
197
+ expect(dispatched).toEqual([]);
198
+ expect(warn).toHaveBeenCalled();
199
+ warn.mockRestore();
200
+ });
201
+ });
@@ -1,5 +1,77 @@
1
1
  import { getSdkCall } from "../internal/sdkCall.js";
2
2
  const DB_INTEGRATION_ID = "databases";
3
+ function isPlainObject(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+ /**
7
+ * Not thrown — the miss is the answer. Logged so the author can still find out
8
+ * their value was missing, since a silent null looks exactly like a legitimate
9
+ * no-match and would otherwise hide the bug that caused it.
10
+ */
11
+ function warnEmptySelector(className, method, detail) {
12
+ console.warn(`${className}.${method}: ${detail} is undefined, so this query would have ` +
13
+ "run unnarrowed and returned an arbitrary record. Returning no match " +
14
+ "instead. Check the value before querying, or pass null to match " +
15
+ "records where the field is empty.");
16
+ }
17
+ /**
18
+ * Whether a selector was written with nothing in it.
19
+ *
20
+ * `JSON.stringify` drops an undefined value, so `{ filters: { token: undefined } }`
21
+ * reaches base-runner as `{ filters: {} }` — a valid request for no filter.
22
+ * `findOne` answers that with the first row of the table, and the caller reads
23
+ * it back as a match.
24
+ *
25
+ * `findOne` treats it as "matched nothing" instead, which is what asking for
26
+ * `token === undefined` should mean. Callers already handle the miss —
27
+ * `if (!row) return { status: 'invalid_token' }` — so the shape that caused the
28
+ * incident becomes correct on its own, with no app change and nothing to break.
29
+ *
30
+ * **`findOne` only.** On `findAll`, "no filter" is a coherent request, and
31
+ * `filters: { status: maybeUndefined }` meaning "don't narrow on status" is an
32
+ * idiom that type-checks today (`RecordFilters` is a `?:` map, and apps build
33
+ * without `exactOptionalPropertyTypes`). It is also what Prisma does with
34
+ * `undefined` in a `where`. Only `findOne` has no sane reading, because an
35
+ * unnarrowed single-row query returns an arbitrary record rather than everything.
36
+ *
37
+ * A key the caller never wrote is untouched: `findOne({ filters: {} })` still
38
+ * means "no filter" and still returns the first row. Only a key written with
39
+ * nothing in it counts. (Unknown filter keys have the same consequence — see
40
+ * `RecordFilters` — but those are `keyof T` and caught at build time.)
41
+ */
42
+ function selectorMatchesNothing(className, method, params) {
43
+ if (!params)
44
+ return false;
45
+ const { filters } = params;
46
+ if (isPlainObject(filters)) {
47
+ for (const [key, value] of Object.entries(filters)) {
48
+ if (value === undefined) {
49
+ warnEmptySelector(className, method, `filter "${key}"`);
50
+ return true;
51
+ }
52
+ // `{ field: { contains: undefined } }` collapses to `{ field: {} }` — no
53
+ // condition on that field at all.
54
+ if (isPlainObject(value)) {
55
+ for (const [operator, operand] of Object.entries(value)) {
56
+ if (operand === undefined) {
57
+ warnEmptySelector(className, method, `filter "${key}.${operator}"`);
58
+ return true;
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ // An undefined `id` leaves the query with no selector at all. Only counts
65
+ // when nothing else narrows it, so `findOne({ id: maybeId, filters: {...} })`
66
+ // still runs on the filters.
67
+ const wroteId = Object.prototype.hasOwnProperty.call(params, "id");
68
+ const hasFilters = isPlainObject(filters) && Object.keys(filters).length > 0;
69
+ if (wroteId && params.id === undefined && !hasFilters) {
70
+ warnEmptySelector(className, method, "`id`");
71
+ return true;
72
+ }
73
+ return false;
74
+ }
3
75
  function getBaseId() {
4
76
  try {
5
77
  return (globalThis.__ZITE_EXECUTION_CONFIG__?.baseId ??
@@ -26,16 +98,22 @@ function resolveTableId(className) {
26
98
  }
27
99
  export function createTableClient(className) {
28
100
  return {
29
- findAll: (params) => getSdkCall()(DB_INTEGRATION_ID, className, "findAll", {
30
- baseId: getBaseId(),
31
- tableId: resolveTableId(className),
32
- ...params,
33
- }),
34
- findOne: (params) => getSdkCall()(DB_INTEGRATION_ID, className, "findOne", {
35
- baseId: getBaseId(),
36
- tableId: resolveTableId(className),
37
- ...params,
38
- }),
101
+ findAll: async (params) => {
102
+ return getSdkCall()(DB_INTEGRATION_ID, className, "findAll", {
103
+ baseId: getBaseId(),
104
+ tableId: resolveTableId(className),
105
+ ...params,
106
+ });
107
+ },
108
+ findOne: async (params) => {
109
+ if (selectorMatchesNothing(className, "findOne", params))
110
+ return undefined;
111
+ return getSdkCall()(DB_INTEGRATION_ID, className, "findOne", {
112
+ baseId: getBaseId(),
113
+ tableId: resolveTableId(className),
114
+ ...params,
115
+ });
116
+ },
39
117
  create: (params) => getSdkCall()(DB_INTEGRATION_ID, className, "create", {
40
118
  baseId: getBaseId(),
41
119
  tableId: resolveTableId(className),
@@ -66,10 +144,12 @@ export function createSqlClient() {
66
144
  }
67
145
  export function createAuthClient() {
68
146
  return {
69
- findAllUsers: (options) => getSdkCall()(DB_INTEGRATION_ID, "Auth", "findAllUsers", {
70
- baseId: getBaseId(),
71
- ...options,
72
- }),
147
+ findAllUsers: async (options) => {
148
+ return getSdkCall()(DB_INTEGRATION_ID, "Auth", "findAllUsers", {
149
+ baseId: getBaseId(),
150
+ ...options,
151
+ });
152
+ },
73
153
  updateUserProfile: (userId, profile) => getSdkCall()(DB_INTEGRATION_ID, "Auth", "updateUserProfile", {
74
154
  baseId: getBaseId(),
75
155
  userId,
@@ -79,14 +159,20 @@ export function createAuthClient() {
79
159
  }
80
160
  export function createAirtableClient(integrationId, className, implicitParams) {
81
161
  return {
82
- findAll: (params) => getSdkCall()(integrationId, className, "findAll", {
83
- ...implicitParams,
84
- ...params,
85
- }),
86
- findOne: (params) => getSdkCall()(integrationId, className, "findOne", {
87
- ...implicitParams,
88
- ...params,
89
- }),
162
+ findAll: async (params) => {
163
+ return getSdkCall()(integrationId, className, "findAll", {
164
+ ...implicitParams,
165
+ ...params,
166
+ });
167
+ },
168
+ findOne: async (params) => {
169
+ if (selectorMatchesNothing(className, "findOne", params))
170
+ return undefined;
171
+ return getSdkCall()(integrationId, className, "findOne", {
172
+ ...implicitParams,
173
+ ...params,
174
+ });
175
+ },
90
176
  create: (params) => getSdkCall()(integrationId, className, "create", {
91
177
  ...implicitParams,
92
178
  ...params,
@@ -0,0 +1 @@
1
+ export {};