@supabase/postgrest-typegen 0.4.0 → 0.5.0

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.
@@ -1,5 +1,5 @@
1
- import { VALID_FUNCTION_ARGS_MODE, VALID_UNNAMED_FUNCTION_ARG_TYPES, } from "./constants.js";
2
1
  import { compareStrings } from "../collation.js";
2
+ import { resolveRpcFunctions } from "../rpc.js";
3
3
  const loadOxfmt = async () => {
4
4
  try {
5
5
  return await import("oxfmt");
@@ -28,8 +28,13 @@ const defaultFormat = async (code) => {
28
28
  * can't be expressed by the single-collection `sortGeneratorMetadata` pass.
29
29
  */
30
30
  export const generateTypescript = async (metadata, opts = {}) => {
31
- const { schemas, tables, foreignTables, views, materializedViews, columns, relationships, functions, types, } = metadata;
31
+ const { schemas, tables, foreignTables, views, materializedViews, columns, relationships, types, } = metadata;
32
+ // Documents predating the collection (still valid version 1) resolve it here.
33
+ const rpcFunctions = metadata.rpcFunctions ?? resolveRpcFunctions(metadata);
32
34
  const { detectOneToOneRelationships = false, postgrestVersion, defaultSchema = "public", format = defaultFormat, } = opts;
35
+ const resolvedDefaultSchema = schemas.some(({ name }) => name === defaultSchema) || !schemas[0]
36
+ ? defaultSchema
37
+ : schemas[0].name;
33
38
  // Every relation-typed value resolves against the same collections, so build
34
39
  // the lookup context once rather than reassembling it at each call site.
35
40
  const typeContext = {
@@ -47,8 +52,7 @@ export const generateTypescript = async (metadata, opts = {}) => {
47
52
  { tables: [], views: [], functions: [], enums: [], compositeTypes: [] },
48
53
  ]));
49
54
  const columnsByTableId = {};
50
- const tablesNamesByTableId = {};
51
- const relationTypeByIds = new Map();
55
+ const relationsById = new Map();
52
56
  // group types by id for quicker lookup
53
57
  const typesById = new Map();
54
58
  const tablesLike = [
@@ -59,7 +63,7 @@ export const generateTypescript = async (metadata, opts = {}) => {
59
63
  ];
60
64
  for (const tableLike of tablesLike) {
61
65
  columnsByTableId[tableLike.id] = [];
62
- tablesNamesByTableId[tableLike.id] = tableLike.name;
66
+ relationsById.set(tableLike.id, tableLike);
63
67
  }
64
68
  for (const column of columns) {
65
69
  columnsByTableId[column.table_id]?.push(column);
@@ -80,10 +84,6 @@ export const generateTypescript = async (metadata, opts = {}) => {
80
84
  };
81
85
  for (const type of types) {
82
86
  typesById.set(type.id, type);
83
- // Save all the types that are relation types for quicker lookup
84
- if (type.type_relation_id) {
85
- relationTypeByIds.set(type.id, type);
86
- }
87
87
  if (type.enums.length > 0) {
88
88
  introspectionBySchema[type.schema]?.enums.push(type);
89
89
  }
@@ -146,51 +146,8 @@ export const generateTypescript = async (metadata, opts = {}) => {
146
146
  relationships: getRelationships(materializedView),
147
147
  });
148
148
  }
149
- // Helper function to get table/view name from relation id
150
- const getTableNameFromRelationId = (relationId, returnTypeId) => {
151
- if (!relationId)
152
- return null;
153
- const tableName = tablesNamesByTableId[relationId];
154
- if (tableName)
155
- return tableName;
156
- // if it's a composite type we use the type name as relation name to allow sub-selecting fields of the composite type
157
- const reltype = returnTypeId ? relationTypeByIds.get(returnTypeId) : null;
158
- return reltype ? reltype.name : null;
159
- };
160
- for (const func of functions) {
161
- const schemaIntrospection = introspectionBySchema[func.schema];
162
- if (schemaIntrospection) {
163
- // Get all input args (in, inout, variadic modes)
164
- const inArgs = func.args.filter(({ mode }) => VALID_FUNCTION_ARGS_MODE.has(mode));
165
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
166
- if (
167
- // Case 1: Function has no parameters
168
- inArgs.length === 0 ||
169
- // Case 2: All input args are named
170
- !inArgs.some(({ name }) => name === "") ||
171
- // Case 3: All unnamed args have default values AND are valid types
172
- inArgs.every((arg) => {
173
- if (arg.name === "") {
174
- return (arg.has_default &&
175
- VALID_UNNAMED_FUNCTION_ARG_TYPES.has(arg.type_id));
176
- }
177
- return true;
178
- }) ||
179
- // Case 4: Single unnamed parameter of valid type (json, jsonb, text)
180
- // Exclude all functions definitions that have only one single argument unnamed argument that isn't
181
- // a json/jsonb/text as it won't be considered by PostgREST
182
- (soleInArg &&
183
- soleInArg.name === "" &&
184
- (VALID_UNNAMED_FUNCTION_ARG_TYPES.has(soleInArg.type_id) ||
185
- // OR if the function have a single unnamed args which is another table (embeded function)
186
- (relationTypeByIds.get(soleInArg.type_id) &&
187
- getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id)) ||
188
- // OR if the function takes a table row but doesn't qualify as embedded (for error reporting)
189
- (relationTypeByIds.get(soleInArg.type_id) &&
190
- !getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id))))) {
191
- schemaIntrospection.functions.push({ fn: func, inArgs });
192
- }
193
- }
149
+ for (const fn of rpcFunctions) {
150
+ introspectionBySchema[fn.schema]?.functions.push(fn);
194
151
  }
195
152
  // These per-schema groups MERGE multiple metadata collections — `.tables`
196
153
  // combines tables + foreign tables, `.views` combines views + materialized
@@ -199,83 +156,42 @@ export const generateTypescript = async (metadata, opts = {}) => {
199
156
  for (const schemaIntrospection of Object.values(introspectionBySchema)) {
200
157
  schemaIntrospection.tables.sort((a, b) => compareStrings(a.table.name, b.table.name));
201
158
  schemaIntrospection.views.sort((a, b) => compareStrings(a.view.name, b.view.name));
202
- schemaIntrospection.functions.sort((a, b) => compareStrings(a.fn.name, b.fn.name));
203
159
  schemaIntrospection.enums.sort((a, b) => compareStrings(a.name, b.name));
204
160
  schemaIntrospection.compositeTypes.sort((a, b) => compareStrings(a.name, b.name));
205
161
  }
206
- const getFunctionTsReturnType = (fn, returnType) => {
207
- // Determine if this function should have SetofOptions
208
- let setofOptionsInfo = "";
209
- const returnTableName = getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id);
210
- const returnsSetOfTable = fn.is_set_returning_function && fn.return_type_relation_id !== null;
211
- const returnsMultipleRows = fn.prorows !== null && fn.prorows > 1;
212
- // Case 1: if the function returns a table, we need to add SetofOptions to allow selecting sub fields of the table
213
- // Those can be used in rpc to select sub fields of a table
214
- if (returnTableName) {
215
- setofOptionsInfo = `SetofOptions: {
216
- from: "*"
217
- to: ${JSON.stringify(returnTableName)}
218
- isOneToOne: ${Boolean(!returnsMultipleRows)}
219
- isSetofReturn: ${fn.is_set_returning_function}
220
- }`;
221
- }
222
- // Case 2: if the function has a single table argument, we need to add SetofOptions to allow selecting sub fields of the table
223
- // and set the right "from" and "to" values to allow selecting from a table row
224
- const soleArg = fn.args.length === 1 ? fn.args[0] : undefined;
225
- if (soleArg) {
226
- const relationType = relationTypeByIds.get(soleArg.type_id);
227
- // Only add SetofOptions for functions with table arguments (embedded functions)
228
- // or specific functions that RETURNS table-name
229
- if (relationType) {
230
- const sourceTable = relationType.format;
231
- // Case 1: Standard embedded function with proper setof detection
232
- if (returnsSetOfTable && returnTableName) {
233
- setofOptionsInfo = `SetofOptions: {
234
- from: ${JSON.stringify(sourceTable)}
235
- to: ${JSON.stringify(returnTableName)}
236
- isOneToOne: ${Boolean(!returnsMultipleRows)}
237
- isSetofReturn: true
238
- }`;
239
- } // Case 2: Handle RETURNS table-name those are always a one to one relationship
240
- else if (returnTableName && !returnsSetOfTable) {
241
- const targetTable = returnTableName;
242
- setofOptionsInfo = `SetofOptions: {
243
- from: ${JSON.stringify(sourceTable)}
244
- to: ${JSON.stringify(targetTable)}
245
- isOneToOne: true
246
- isSetofReturn: false
247
- }`;
248
- }
249
- }
250
- }
251
- // A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted
252
- // (returnTableName resolved): postgrest-js relies on isOneToOne to restore the array shape
253
- // for direct RPC calls. Without that metadata PostgREST still returns an array, so the
254
- // collapse would make the type diverge from the actual response.
255
- const collapsibleSingleRow = !returnsMultipleRows && returnTableName !== null;
256
- return `${returnType}${fn.is_set_returning_function && !collapsibleSingleRow ? "[]" : ""}
257
- ${setofOptionsInfo ? `${setofOptionsInfo}` : ""}`;
162
+ const referencedTsType = (schema, { format, type_schema }) => format === null || type_schema === null
163
+ ? "unknown"
164
+ : pgTypeToTsType(schema, format, typeContext, type_schema);
165
+ const getFunctionTsReturnType = (overload, returnType) => {
166
+ const { setof_options } = overload;
167
+ const setofOptionsInfo = setof_options
168
+ ? `SetofOptions: {
169
+ from: ${JSON.stringify(setof_options.from)}
170
+ to: ${JSON.stringify(setof_options.to)}
171
+ isOneToOne: ${setof_options.is_one_to_one}
172
+ isSetofReturn: ${setof_options.is_setof_return}
173
+ }`
174
+ : "";
175
+ // A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted:
176
+ // postgrest-js relies on isOneToOne to restore the array shape for direct RPC calls.
177
+ // Without that metadata PostgREST still returns an array, so the collapse would make
178
+ // the type diverge from the actual response.
179
+ const collapsibleSingleRow = setof_options?.is_one_to_one === true;
180
+ return `${returnType}${overload.is_set_returning && !collapsibleSingleRow ? "[]" : ""}
181
+ ${setofOptionsInfo}`;
258
182
  };
259
- const getFunctionReturnType = (schema, fn) => {
183
+ const getFunctionReturnType = (schema, { returns }) => {
260
184
  // Case 1: `returns table`.
261
- const tableArgs = fn.args.filter(({ mode }) => mode === "table");
262
- if (tableArgs.length > 0) {
263
- const argsNameAndType = tableArgs.map(({ name, type_id }) => {
264
- const type = typesById.get(type_id);
265
- let tsType = "unknown";
266
- if (type) {
267
- tsType = pgTypeToTsType(schema, type.name, typeContext, type.schema);
268
- }
269
- return { name, type: tsType };
270
- });
185
+ if (returns.columns) {
271
186
  return `{
272
- ${argsNameAndType.map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)}
187
+ ${returns.columns.map((column) => `${JSON.stringify(column.name)}: ${referencedTsType(schema, column)}`)}
273
188
  }`;
274
189
  }
275
- // Case 2: returns a relation's row type.
276
- const relation = introspectionBySchema[schema.name]?.tables.find(({ table: { id } }) => id === fn.return_type_relation_id)?.table ||
277
- introspectionBySchema[schema.name]?.views.find(({ view: { id } }) => id === fn.return_type_relation_id)?.view;
278
- if (relation) {
190
+ // Case 2: returns a row type of a relation in this schema.
191
+ const relation = returns.relation_id === null
192
+ ? undefined
193
+ : relationsById.get(returns.relation_id);
194
+ if (relation && relation.schema === schema.name) {
279
195
  return `{
280
196
  ${getColumns(relation.id)
281
197
  .map((column) => generateColumnTsDefinition(schema, {
@@ -289,120 +205,41 @@ export const generateTypescript = async (metadata, opts = {}) => {
289
205
  }`;
290
206
  }
291
207
  // Case 3: returns base/array/composite/enum type.
292
- const type = typesById.get(fn.return_type_id);
293
- if (type) {
294
- return pgTypeToTsType(schema, type.name, typeContext, type.schema);
295
- }
296
- return "unknown";
208
+ return referencedTsType(schema, returns);
297
209
  };
298
- // Special error case for functions that take table row but don't qualify as embedded functions
299
- const hasTableRowError = (fn, inArgs) => {
300
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
301
- if (soleInArg &&
302
- soleInArg.name === "" &&
303
- relationTypeByIds.get(soleInArg.type_id) &&
304
- !getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id)) {
305
- return true;
306
- }
307
- return false;
308
- };
309
- // Check for generic conflict cases that need error reporting
310
- const getConflictError = (schema, fns, fn, inArgs) => {
311
- // If there is a single function definition, there is no conflict
312
- if (fns.length <= 1)
313
- return null;
314
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
315
- // Generic conflict detection patterns
316
- // Pattern 1: No-args vs default-args conflicts
317
- if (inArgs.length === 0) {
318
- const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
319
- if (otherFn === fn)
320
- return false;
321
- const otherSoleInArg = otherInArgs.length === 1 ? otherInArgs[0] : undefined;
322
- return (otherSoleInArg &&
323
- otherSoleInArg.name === "" &&
324
- otherSoleInArg.has_default);
325
- });
326
- const conflictingFn = conflictingFns[0];
327
- if (conflictingFn) {
328
- const returnTypeName = typesById.get(conflictingFn.fn.return_type_id)?.name || "unknown";
329
- return `Could not choose the best candidate function between: ${schema.name}.${fn.name}(), ${schema.name}.${fn.name}( => ${returnTypeName}). Try renaming the parameters or the function itself in the database so function overloading can be resolved`;
330
- }
331
- }
332
- // Pattern 2: Same parameter name but different types (unresolvable overloads)
333
- if (soleInArg && soleInArg.name !== "") {
334
- const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
335
- if (otherFn === fn)
336
- return false;
337
- const otherSoleInArg = otherInArgs.length === 1 ? otherInArgs[0] : undefined;
338
- return (otherSoleInArg &&
339
- otherSoleInArg.name === soleInArg.name &&
340
- otherSoleInArg.type_id !== soleInArg.type_id);
341
- });
342
- if (conflictingFns.length > 0) {
343
- const allConflictingFunctions = [{ fn, inArgs }, ...conflictingFns];
344
- const conflictList = allConflictingFunctions
345
- .sort((a, b) => {
346
- const aArgs = a.inArgs;
347
- const bArgs = b.inArgs;
348
- return (aArgs[0]?.type_id || 0) - (bArgs[0]?.type_id || 0);
349
- })
350
- .map((f) => {
351
- const args = f.inArgs;
352
- return `${schema.name}.${fn.name}(${args
353
- .map((a) => `${a.name || ""} => ${typesById.get(a.type_id)?.name || "unknown"}`)
354
- .join(", ")})`;
355
- })
356
- .join(", ");
357
- return `Could not choose the best candidate function between: ${conflictList}. Try renaming the parameters or the function itself in the database so function overloading can be resolved`;
358
- }
359
- }
360
- return null;
361
- };
362
- const getArgTsType = (schema, { name, type_id }) => {
363
- const type = typesById.get(type_id);
364
- if (!type) {
365
- return "unknown";
366
- }
367
- const tsType = pgTypeToTsType(schema, type.name, typeContext, type.schema);
210
+ const getArgTsType = (schema, arg) => {
211
+ const tsType = referencedTsType(schema, arg);
368
212
  // A row-typed parameter takes the relation's columns: PostgREST fills the
369
213
  // keys it does not receive with NULL, and the computed fields are never
370
214
  // input. A named parameter is typed without them. An unnamed one keeps the
371
215
  // plain `Row` reference, which is how postgrest-js versions predating
372
216
  // `ComputedFields` recognise the function as a computed field.
373
- if (name === "") {
217
+ if (arg.name === "" || arg.format === null || arg.type_schema === null) {
374
218
  return tsType;
375
219
  }
376
- const relationReference = findRelationReference(type.name, typeContext, type.schema);
220
+ const relationReference = findRelationReference(arg.format, typeContext, arg.type_schema);
377
221
  if (relationReference && tsType === `${relationReference}['Row']`) {
378
222
  return `Omit<${tsType}, ${relationReference}['ComputedFields']>`;
379
223
  }
380
224
  return tsType;
381
225
  };
382
- const getArgsTsType = (schema, inArgs) => `{ ${inArgs.map((arg) => `${JSON.stringify(arg.name)}${arg.has_default ? "?" : ""}: ${getArgTsType(schema, arg)}`)} }`;
383
- const getFunctionSignatures = (schema, fns) => {
384
- return fns
385
- .map(({ fn, inArgs }) => {
226
+ const getArgsTsType = (schema, args) => `{ ${args.map((arg) => `${JSON.stringify(arg.name)}${arg.has_default ? "?" : ""}: ${getArgTsType(schema, arg)}`)} }`;
227
+ const getFunctionSignatures = (schema, fn) => {
228
+ return fn.overloads
229
+ .map((overload) => {
386
230
  // `never` would be wrong here: postgrest-js treats `Args extends { '': Row }`
387
231
  // as a computed field (`never extends T` is always true, so same-named table
388
232
  // columns get omitted from select results), and an object type with a required
389
233
  // `never` property is uninhabited for any tool doing sound type math on the
390
234
  // generated types. `Record<PropertyKey, never>` is the accurate type for
391
235
  // "callable with no arguments".
392
- const argsType = inArgs.length > 0
393
- ? getArgsTsType(schema, inArgs)
236
+ const argsType = overload.args.length > 0
237
+ ? getArgsTsType(schema, overload.args)
394
238
  : "Record<PropertyKey, never>";
395
- let returnType = getFunctionReturnType(schema, fn);
396
- // Check for specific error cases
397
- const conflictError = getConflictError(schema, fns, fn, inArgs);
398
- if (conflictError) {
399
- returnType = `{ error: true } & ${JSON.stringify(conflictError)}`;
400
- }
401
- else if (hasTableRowError(fn, inArgs)) {
402
- // Special case for computed fields returning scalars functions
403
- returnType = `{ error: true } & ${JSON.stringify(`the function ${schema.name}.${fn.name} with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache`)}`;
404
- }
405
- return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(fn, returnType)} }`;
239
+ const returnType = overload.error === null
240
+ ? getFunctionReturnType(schema, overload)
241
+ : `{ error: true } & ${JSON.stringify(overload.error)}`;
242
+ return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(overload, returnType)} }`;
406
243
  })
407
244
  .join(" |\n");
408
245
  };
@@ -429,62 +266,21 @@ export const generateTypescript = async (metadata, opts = {}) => {
429
266
  return `${JSON.stringify(column.name)}${column.is_optional ? "?" : ""}: ${generateNullableUnionTsType(pgTypeToTsType(schema, column.format, context, column.type_schema), column.is_nullable)}`;
430
267
  }
431
268
  /**
432
- * Is `fn` a PostgREST computed field on `relation` (a table or view)?
433
- *
434
- * A computed field is a function taking a single argument of the relation's
435
- * own composite type, so it is selectable as if it were a column and belongs
436
- * in `Row`.
437
- *
438
- * Matching on the argument's type OID rather than on `argument_types` is what
439
- * makes named parameters work. `argument_types` is
440
- * `pg_get_function_arguments()`, which renders the parameter *name* when the
441
- * parameter has one — `category category` rather than `category` — and
442
- * schema-qualifies the type when it is not visible in `search_path`. Both
443
- * forms failed a bare string comparison against the relation name, so
444
- * `create function name_translated(category category)` silently lost its
445
- * computed field while the unnamed `name_translated(category)` kept it.
446
- * See supabase/postgres-meta#1034.
269
+ * The computed fields of a relation, in function name order. A computed
270
+ * field is a function taking a single argument of the relation's own
271
+ * composite type, so it is selectable as if it were a column and belongs in
272
+ * `Row`; `resolveRpcFunctions` marks them with `computed_field_of`.
447
273
  */
448
- const isComputedFieldOf = (fn, inArgs, relation) => {
449
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
450
- if (soleInArg) {
451
- // `relationTypeByIds` is already "types backed by a relation", so a hit
452
- // identifies the relation regardless of how the argument was spelled.
453
- // A `variadic` argument is an array type and never appears here.
454
- const argType = relationTypeByIds.get(soleInArg.type_id);
455
- if (argType) {
456
- return argType.type_relation_id === relation.id;
457
- }
458
- }
459
- // Fall back to the historical comparison when the argument's type is not in
460
- // the metadata (callers may pass a reduced `types` list), so a computed
461
- // field that resolved before keeps resolving.
462
- return fn.argument_types === relation.name;
463
- };
464
- /**
465
- * The computed fields of a relation, in `schemaFunctions` order. A function
466
- * named like one of the relation's columns is left out: PostgREST resolves
467
- * the name to the column, so it is neither selectable nor part of `Row`.
468
- */
469
- const computedFieldsOf = (schemaFunctions, relation) => {
470
- const columnNames = new Set(getColumns(relation.id).map((column) => column.name));
471
- const seen = new Set();
472
- return schemaFunctions.filter(({ fn, inArgs }) => {
473
- if (!isComputedFieldOf(fn, inArgs, relation) ||
474
- columnNames.has(fn.name) ||
475
- seen.has(fn.name)) {
476
- return false;
477
- }
478
- seen.add(fn.name);
479
- return true;
480
- });
481
- };
482
- const computedFieldTsDefinition = (schema, { fn }) => `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}`;
274
+ const computedFieldsOf = (schemaFunctions, relation) => schemaFunctions.flatMap((fn) => {
275
+ const overload = fn.overloads.find(({ computed_field_of }) => computed_field_of === relation.id);
276
+ return overload ? [{ name: fn.name, overload }] : [];
277
+ });
278
+ const computedFieldTsDefinition = (schema, { name, overload }) => `${JSON.stringify(name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, overload), true)}`;
483
279
  // `ComputedFields` names the `Row` keys PostgREST leaves out of `select('*')`,
484
280
  // so postgrest-js does not have to infer them from `Functions`.
485
281
  const computedFieldsTsType = (computedFields) => computedFields.length === 0
486
282
  ? "never"
487
- : computedFields.map(({ fn }) => JSON.stringify(fn.name)).join(" | ");
283
+ : computedFields.map(({ name }) => JSON.stringify(name)).join(" | ");
488
284
  let output = `
489
285
  export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
490
286
 
@@ -611,25 +407,11 @@ export type Database = {
611
407
  })}
612
408
  }
613
409
  Functions: {
614
- ${(() => {
615
- if (schemaFunctions.length === 0) {
616
- return "[_ in never]: never";
617
- }
618
- const schemaFunctionsGroupedByName = schemaFunctions.reduce((acc, curr) => {
619
- (acc[curr.fn.name] ??= []).push(curr);
620
- return acc;
621
- }, {});
622
- for (const fns of Object.values(schemaFunctionsGroupedByName)) {
623
- fns.sort((a, b) => compareStrings(a.fn.argument_types, b.fn.argument_types) ||
624
- compareStrings(a.fn.return_type, b.fn.return_type));
625
- }
626
- return Object.entries(schemaFunctionsGroupedByName)
627
- .map(([fnName, fns]) => {
628
- const functionSignatures = getFunctionSignatures(schema, fns);
629
- return `${JSON.stringify(fnName)}:\n${functionSignatures}`;
630
- })
631
- .join(",\n");
632
- })()}
410
+ ${schemaFunctions.length === 0
411
+ ? "[_ in never]: never"
412
+ : schemaFunctions
413
+ .map((fn) => `${JSON.stringify(fn.name)}:\n${getFunctionSignatures(schema, fn)}`)
414
+ .join(",\n")}
633
415
  }
634
416
  Enums: {
635
417
  ${schemaEnums.length === 0
@@ -658,7 +440,7 @@ export type Database = {
658
440
 
659
441
  type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
660
442
 
661
- type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(defaultSchema)}>]
443
+ type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(resolvedDefaultSchema)}>]
662
444
 
663
445
  export type Tables<
664
446
  DefaultSchemaTableNameOrOptions extends
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types.ts";
2
2
  export * from "./sort.ts";
3
+ export * from "./rpc.ts";
3
4
  export * from "./introspection/index.ts";
4
5
  export * from "./generation/index.ts";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types.js";
2
2
  export * from "./sort.js";
3
+ export * from "./rpc.js";
3
4
  export * from "./introspection/index.js";
4
5
  export * from "./generation/index.js";
@@ -1,4 +1,5 @@
1
1
  import { GENERATOR_METADATA_VERSION, } from "../types.js";
2
+ import { resolveRpcFunctions } from "../rpc.js";
2
3
  import { normalizeRows } from "./normalize.js";
3
4
  import { listRelationships } from "./relationships.js";
4
5
  import { COLUMNS_SQL } from "./sql/columns.sql.js";
@@ -43,7 +44,7 @@ export async function introspect(db, opts = {}) {
43
44
  queryRows(FUNCTIONS_SQL({ schemaFilter: systemExcludingFilter })),
44
45
  queryRows(TYPES_SQL),
45
46
  ]);
46
- return {
47
+ const metadata = {
47
48
  version: GENERATOR_METADATA_VERSION,
48
49
  schemas,
49
50
  tables,
@@ -56,4 +57,5 @@ export async function introspect(db, opts = {}) {
56
57
  functions: functions.filter(({ return_type }) => !["trigger", "event_trigger"].includes(return_type)),
57
58
  types,
58
59
  };
60
+ return { ...metadata, rpcFunctions: resolveRpcFunctions(metadata) };
59
61
  }
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { GeneratorMetadata, RpcFunction } from "./types.ts";
2
+ export declare function resolveRpcFunctions(metadata: GeneratorMetadata): RpcFunction[];