@supabase/postgrest-typegen 0.4.0 → 0.6.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.
@@ -7,18 +7,17 @@ import type {
7
7
  GeneratorMetadata,
8
8
  PostgresColumn,
9
9
  PostgresForeignTable,
10
- PostgresFunction,
11
10
  PostgresMaterializedView,
12
11
  PostgresSchema,
13
12
  PostgresTable,
14
13
  PostgresType,
15
14
  PostgresView,
15
+ RpcFunction,
16
+ RpcFunctionOverload,
17
+ RpcTypeReference,
16
18
  } from "../types.ts";
17
- import {
18
- VALID_FUNCTION_ARGS_MODE,
19
- VALID_UNNAMED_FUNCTION_ARG_TYPES,
20
- } from "./constants.ts";
21
19
  import { compareStrings } from "../collation.ts";
20
+ import { resolveRpcFunctions } from "../rpc.ts";
22
21
 
23
22
  export interface GenerateTypescriptOptions {
24
23
  /**
@@ -34,7 +33,8 @@ export interface GenerateTypescriptOptions {
34
33
  /**
35
34
  * Schema treated as the default for the generated `Tables`/`Enums`/etc.
36
35
  * helper types. Replaces postgres-meta's `GENERATE_TYPES_DEFAULT_SCHEMA`
37
- * env read. Default `'public'`.
36
+ * env read. Default `'public'`. When it is not among the generated schemas,
37
+ * the first generated schema is used instead, so the helpers keep resolving.
38
38
  */
39
39
  defaultSchema?: string;
40
40
  /**
@@ -119,15 +119,19 @@ export const generateTypescript = async (
119
119
  materializedViews,
120
120
  columns,
121
121
  relationships,
122
- functions,
123
122
  types,
124
123
  } = metadata;
124
+ const rpcFunctions = metadata.rpcFunctions ?? resolveRpcFunctions(metadata);
125
125
  const {
126
126
  detectOneToOneRelationships = false,
127
127
  postgrestVersion,
128
128
  defaultSchema = "public",
129
129
  format = defaultFormat,
130
130
  } = opts;
131
+ const resolvedDefaultSchema =
132
+ schemas.some(({ name }) => name === defaultSchema) || !schemas[0]
133
+ ? defaultSchema
134
+ : schemas[0].name;
131
135
  // Every relation-typed value resolves against the same collections, so build
132
136
  // the lookup context once rather than reassembling it at each call site.
133
137
  const typeContext: TypeResolutionContext = {
@@ -149,7 +153,7 @@ export const generateTypescript = async (
149
153
  view: PostgresView;
150
154
  relationships: TsRelationship[];
151
155
  }[];
152
- functions: { fn: PostgresFunction; inArgs: PostgresFunction["args"] }[];
156
+ functions: RpcFunction[];
153
157
  enums: PostgresType[];
154
158
  compositeTypes: PostgresType[];
155
159
  }>(
@@ -159,8 +163,7 @@ export const generateTypescript = async (
159
163
  ]),
160
164
  );
161
165
  const columnsByTableId: Record<number, PostgresColumn[]> = {};
162
- const tablesNamesByTableId: Record<number, string> = {};
163
- const relationTypeByIds = new Map<number, (typeof types)[number]>();
166
+ const relationsById = new Map<number, { id: number; schema: string }>();
164
167
  // group types by id for quicker lookup
165
168
  const typesById = new Map<number, (typeof types)[number]>();
166
169
  const tablesLike = [
@@ -172,7 +175,7 @@ export const generateTypescript = async (
172
175
 
173
176
  for (const tableLike of tablesLike) {
174
177
  columnsByTableId[tableLike.id] = [];
175
- tablesNamesByTableId[tableLike.id] = tableLike.name;
178
+ relationsById.set(tableLike.id, tableLike);
176
179
  }
177
180
  for (const column of columns) {
178
181
  columnsByTableId[column.table_id]?.push(column);
@@ -198,10 +201,6 @@ export const generateTypescript = async (
198
201
 
199
202
  for (const type of types) {
200
203
  typesById.set(type.id, type);
201
- // Save all the types that are relation types for quicker lookup
202
- if (type.type_relation_id) {
203
- relationTypeByIds.set(type.id, type);
204
- }
205
204
  if (type.enums.length > 0) {
206
205
  introspectionBySchema[type.schema]?.enums.push(type);
207
206
  }
@@ -288,66 +287,8 @@ export const generateTypescript = async (
288
287
  relationships: getRelationships(materializedView),
289
288
  });
290
289
  }
291
- // Helper function to get table/view name from relation id
292
- const getTableNameFromRelationId = (
293
- relationId: number | null,
294
- returnTypeId: number | null,
295
- ): string | null => {
296
- if (!relationId) return null;
297
-
298
- const tableName = tablesNamesByTableId[relationId];
299
- if (tableName) return tableName;
300
- // if it's a composite type we use the type name as relation name to allow sub-selecting fields of the composite type
301
- const reltype = returnTypeId ? relationTypeByIds.get(returnTypeId) : null;
302
- return reltype ? reltype.name : null;
303
- };
304
-
305
- for (const func of functions) {
306
- const schemaIntrospection = introspectionBySchema[func.schema];
307
- if (schemaIntrospection) {
308
- // Get all input args (in, inout, variadic modes)
309
- const inArgs = func.args.filter(({ mode }) =>
310
- VALID_FUNCTION_ARGS_MODE.has(mode),
311
- );
312
-
313
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
314
- if (
315
- // Case 1: Function has no parameters
316
- inArgs.length === 0 ||
317
- // Case 2: All input args are named
318
- !inArgs.some(({ name }) => name === "") ||
319
- // Case 3: All unnamed args have default values AND are valid types
320
- inArgs.every((arg) => {
321
- if (arg.name === "") {
322
- return (
323
- arg.has_default &&
324
- VALID_UNNAMED_FUNCTION_ARG_TYPES.has(arg.type_id)
325
- );
326
- }
327
- return true;
328
- }) ||
329
- // Case 4: Single unnamed parameter of valid type (json, jsonb, text)
330
- // Exclude all functions definitions that have only one single argument unnamed argument that isn't
331
- // a json/jsonb/text as it won't be considered by PostgREST
332
- (soleInArg &&
333
- soleInArg.name === "" &&
334
- (VALID_UNNAMED_FUNCTION_ARG_TYPES.has(soleInArg.type_id) ||
335
- // OR if the function have a single unnamed args which is another table (embeded function)
336
- (relationTypeByIds.get(soleInArg.type_id) &&
337
- getTableNameFromRelationId(
338
- func.return_type_relation_id,
339
- func.return_type_id,
340
- )) ||
341
- // OR if the function takes a table row but doesn't qualify as embedded (for error reporting)
342
- (relationTypeByIds.get(soleInArg.type_id) &&
343
- !getTableNameFromRelationId(
344
- func.return_type_relation_id,
345
- func.return_type_id,
346
- ))))
347
- ) {
348
- schemaIntrospection.functions.push({ fn: func, inArgs });
349
- }
350
- }
290
+ for (const fn of rpcFunctions) {
291
+ introspectionBySchema[fn.schema]?.functions.push(fn);
351
292
  }
352
293
  // These per-schema groups MERGE multiple metadata collections — `.tables`
353
294
  // combines tables + foreign tables, `.views` combines views + materialized
@@ -360,110 +301,66 @@ export const generateTypescript = async (
360
301
  schemaIntrospection.views.sort((a, b) =>
361
302
  compareStrings(a.view.name, b.view.name),
362
303
  );
363
- schemaIntrospection.functions.sort((a, b) =>
364
- compareStrings(a.fn.name, b.fn.name),
365
- );
366
304
  schemaIntrospection.enums.sort((a, b) => compareStrings(a.name, b.name));
367
305
  schemaIntrospection.compositeTypes.sort((a, b) =>
368
306
  compareStrings(a.name, b.name),
369
307
  );
370
308
  }
371
309
 
310
+ const referencedTsType = (
311
+ schema: PostgresSchema,
312
+ { format, type_schema }: RpcTypeReference,
313
+ ) =>
314
+ format === null || type_schema === null
315
+ ? "unknown"
316
+ : pgTypeToTsType(schema, format, typeContext, type_schema);
317
+
372
318
  const getFunctionTsReturnType = (
373
- fn: PostgresFunction,
319
+ overload: RpcFunctionOverload,
374
320
  returnType: string,
375
321
  ) => {
376
- // Determine if this function should have SetofOptions
377
- let setofOptionsInfo = "";
378
-
379
- const returnTableName = getTableNameFromRelationId(
380
- fn.return_type_relation_id,
381
- fn.return_type_id,
382
- );
383
- const returnsSetOfTable =
384
- fn.is_set_returning_function && fn.return_type_relation_id !== null;
385
- const returnsMultipleRows = fn.prorows !== null && fn.prorows > 1;
386
- // Case 1: if the function returns a table, we need to add SetofOptions to allow selecting sub fields of the table
387
- // Those can be used in rpc to select sub fields of a table
388
- if (returnTableName) {
389
- setofOptionsInfo = `SetofOptions: {
390
- from: "*"
391
- to: ${JSON.stringify(returnTableName)}
392
- isOneToOne: ${Boolean(!returnsMultipleRows)}
393
- isSetofReturn: ${fn.is_set_returning_function}
394
- }`;
395
- }
396
- // Case 2: if the function has a single table argument, we need to add SetofOptions to allow selecting sub fields of the table
397
- // and set the right "from" and "to" values to allow selecting from a table row
398
- const soleArg = fn.args.length === 1 ? fn.args[0] : undefined;
399
- if (soleArg) {
400
- const relationType = relationTypeByIds.get(soleArg.type_id);
401
-
402
- // Only add SetofOptions for functions with table arguments (embedded functions)
403
- // or specific functions that RETURNS table-name
404
- if (relationType) {
405
- const sourceTable = relationType.format;
406
- // Case 1: Standard embedded function with proper setof detection
407
- if (returnsSetOfTable && returnTableName) {
408
- setofOptionsInfo = `SetofOptions: {
409
- from: ${JSON.stringify(sourceTable)}
410
- to: ${JSON.stringify(returnTableName)}
411
- isOneToOne: ${Boolean(!returnsMultipleRows)}
412
- isSetofReturn: true
413
- }`;
414
- } // Case 2: Handle RETURNS table-name those are always a one to one relationship
415
- else if (returnTableName && !returnsSetOfTable) {
416
- const targetTable = returnTableName;
417
- setofOptionsInfo = `SetofOptions: {
418
- from: ${JSON.stringify(sourceTable)}
419
- to: ${JSON.stringify(targetTable)}
420
- isOneToOne: true
421
- isSetofReturn: false
422
- }`;
423
- }
424
- }
425
- }
426
-
427
- // A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted
428
- // (returnTableName resolved): postgrest-js relies on isOneToOne to restore the array shape
429
- // for direct RPC calls. Without that metadata PostgREST still returns an array, so the
430
- // collapse would make the type diverge from the actual response.
431
- const collapsibleSingleRow =
432
- !returnsMultipleRows && returnTableName !== null;
433
- return `${returnType}${fn.is_set_returning_function && !collapsibleSingleRow ? "[]" : ""}
434
- ${setofOptionsInfo ? `${setofOptionsInfo}` : ""}`;
322
+ const { setof_options } = overload;
323
+ const setofOptionsInfo = setof_options
324
+ ? `SetofOptions: {
325
+ from: ${JSON.stringify(setof_options.from)}
326
+ to: ${JSON.stringify(setof_options.to)}
327
+ isOneToOne: ${setof_options.is_one_to_one}
328
+ isSetofReturn: ${setof_options.is_setof_return}
329
+ }`
330
+ : "";
331
+ // A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted:
332
+ // postgrest-js relies on isOneToOne to restore the array shape for direct RPC calls.
333
+ // Without that metadata PostgREST still returns an array, so the collapse would make
334
+ // the type diverge from the actual response.
335
+ const collapsibleSingleRow = setof_options?.is_one_to_one === true;
336
+ const isArray =
337
+ overload.error === null &&
338
+ overload.is_set_returning &&
339
+ !collapsibleSingleRow;
340
+ return `${returnType}${isArray ? "[]" : ""}
341
+ ${setofOptionsInfo}`;
435
342
  };
436
343
 
437
344
  const getFunctionReturnType = (
438
345
  schema: PostgresSchema,
439
- fn: PostgresFunction,
346
+ { returns }: RpcFunctionOverload,
440
347
  ): string => {
441
348
  // Case 1: `returns table`.
442
- const tableArgs = fn.args.filter(({ mode }) => mode === "table");
443
- if (tableArgs.length > 0) {
444
- const argsNameAndType = tableArgs.map(({ name, type_id }) => {
445
- const type = typesById.get(type_id);
446
- let tsType = "unknown";
447
- if (type) {
448
- tsType = pgTypeToTsType(schema, type.name, typeContext, type.schema);
449
- }
450
- return { name, type: tsType };
451
- });
452
-
349
+ if (returns.columns) {
453
350
  return `{
454
- ${argsNameAndType.map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)}
351
+ ${returns.columns.map(
352
+ (column) =>
353
+ `${JSON.stringify(column.name)}: ${referencedTsType(schema, column)}`,
354
+ )}
455
355
  }`;
456
356
  }
457
357
 
458
- // Case 2: returns a relation's row type.
358
+ // Case 2: returns a row type of a relation in this schema.
459
359
  const relation =
460
- introspectionBySchema[schema.name]?.tables.find(
461
- ({ table: { id } }) => id === fn.return_type_relation_id,
462
- )?.table ||
463
- introspectionBySchema[schema.name]?.views.find(
464
- ({ view: { id } }) => id === fn.return_type_relation_id,
465
- )?.view;
466
- if (relation) {
360
+ returns.relation_id === null
361
+ ? undefined
362
+ : relationsById.get(returns.relation_id);
363
+ if (relation && relation.schema === schema.name) {
467
364
  return `{
468
365
  ${getColumns(relation.id)
469
366
  .map((column) =>
@@ -484,127 +381,26 @@ export const generateTypescript = async (
484
381
  }
485
382
 
486
383
  // Case 3: returns base/array/composite/enum type.
487
- const type = typesById.get(fn.return_type_id);
488
- if (type) {
489
- return pgTypeToTsType(schema, type.name, typeContext, type.schema);
490
- }
491
-
492
- return "unknown";
493
- };
494
- // Special error case for functions that take table row but don't qualify as embedded functions
495
- const hasTableRowError = (
496
- fn: PostgresFunction,
497
- inArgs: PostgresFunction["args"],
498
- ) => {
499
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
500
- if (
501
- soleInArg &&
502
- soleInArg.name === "" &&
503
- relationTypeByIds.get(soleInArg.type_id) &&
504
- !getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id)
505
- ) {
506
- return true;
507
- }
508
- return false;
509
- };
510
-
511
- // Check for generic conflict cases that need error reporting
512
- const getConflictError = (
513
- schema: PostgresSchema,
514
- fns: Array<{ fn: PostgresFunction; inArgs: PostgresFunction["args"] }>,
515
- fn: PostgresFunction,
516
- inArgs: PostgresFunction["args"],
517
- ) => {
518
- // If there is a single function definition, there is no conflict
519
- if (fns.length <= 1) return null;
520
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
521
-
522
- // Generic conflict detection patterns
523
- // Pattern 1: No-args vs default-args conflicts
524
- if (inArgs.length === 0) {
525
- const conflictingFns = fns.filter(
526
- ({ fn: otherFn, inArgs: otherInArgs }) => {
527
- if (otherFn === fn) return false;
528
- const otherSoleInArg =
529
- otherInArgs.length === 1 ? otherInArgs[0] : undefined;
530
- return (
531
- otherSoleInArg &&
532
- otherSoleInArg.name === "" &&
533
- otherSoleInArg.has_default
534
- );
535
- },
536
- );
537
-
538
- const conflictingFn = conflictingFns[0];
539
- if (conflictingFn) {
540
- const returnTypeName =
541
- typesById.get(conflictingFn.fn.return_type_id)?.name || "unknown";
542
- 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`;
543
- }
544
- }
545
-
546
- // Pattern 2: Same parameter name but different types (unresolvable overloads)
547
- if (soleInArg && soleInArg.name !== "") {
548
- const conflictingFns = fns.filter(
549
- ({ fn: otherFn, inArgs: otherInArgs }) => {
550
- if (otherFn === fn) return false;
551
- const otherSoleInArg =
552
- otherInArgs.length === 1 ? otherInArgs[0] : undefined;
553
- return (
554
- otherSoleInArg &&
555
- otherSoleInArg.name === soleInArg.name &&
556
- otherSoleInArg.type_id !== soleInArg.type_id
557
- );
558
- },
559
- );
560
-
561
- if (conflictingFns.length > 0) {
562
- const allConflictingFunctions = [{ fn, inArgs }, ...conflictingFns];
563
- const conflictList = allConflictingFunctions
564
- .sort((a, b) => {
565
- const aArgs = a.inArgs;
566
- const bArgs = b.inArgs;
567
- return (aArgs[0]?.type_id || 0) - (bArgs[0]?.type_id || 0);
568
- })
569
- .map((f) => {
570
- const args = f.inArgs;
571
- return `${schema.name}.${fn.name}(${args
572
- .map(
573
- (a) =>
574
- `${a.name || ""} => ${typesById.get(a.type_id)?.name || "unknown"}`,
575
- )
576
- .join(", ")})`;
577
- })
578
- .join(", ");
579
-
580
- 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`;
581
- }
582
- }
583
-
584
- return null;
384
+ return referencedTsType(schema, returns);
585
385
  };
586
386
 
587
387
  const getArgTsType = (
588
388
  schema: PostgresSchema,
589
- { name, type_id }: PostgresFunction["args"][number],
389
+ arg: RpcFunctionOverload["args"][number],
590
390
  ) => {
591
- const type = typesById.get(type_id);
592
- if (!type) {
593
- return "unknown";
594
- }
595
- const tsType = pgTypeToTsType(schema, type.name, typeContext, type.schema);
391
+ const tsType = referencedTsType(schema, arg);
596
392
  // A row-typed parameter takes the relation's columns: PostgREST fills the
597
393
  // keys it does not receive with NULL, and the computed fields are never
598
394
  // input. A named parameter is typed without them. An unnamed one keeps the
599
395
  // plain `Row` reference, which is how postgrest-js versions predating
600
396
  // `ComputedFields` recognise the function as a computed field.
601
- if (name === "") {
397
+ if (arg.name === "" || arg.format === null || arg.type_schema === null) {
602
398
  return tsType;
603
399
  }
604
400
  const relationReference = findRelationReference(
605
- type.name,
401
+ arg.format,
606
402
  typeContext,
607
- type.schema,
403
+ arg.type_schema,
608
404
  );
609
405
  if (relationReference && tsType === `${relationReference}['Row']`) {
610
406
  return `Omit<${tsType}, ${relationReference}['ComputedFields']>`;
@@ -614,19 +410,16 @@ export const generateTypescript = async (
614
410
 
615
411
  const getArgsTsType = (
616
412
  schema: PostgresSchema,
617
- inArgs: PostgresFunction["args"],
413
+ args: RpcFunctionOverload["args"],
618
414
  ) =>
619
- `{ ${inArgs.map(
415
+ `{ ${args.map(
620
416
  (arg) =>
621
417
  `${JSON.stringify(arg.name)}${arg.has_default ? "?" : ""}: ${getArgTsType(schema, arg)}`,
622
418
  )} }`;
623
419
 
624
- const getFunctionSignatures = (
625
- schema: PostgresSchema,
626
- fns: Array<{ fn: PostgresFunction; inArgs: PostgresFunction["args"] }>,
627
- ) => {
628
- return fns
629
- .map(({ fn, inArgs }) => {
420
+ const getFunctionSignatures = (schema: PostgresSchema, fn: RpcFunction) => {
421
+ return fn.overloads
422
+ .map((overload) => {
630
423
  // `never` would be wrong here: postgrest-js treats `Args extends { '': Row }`
631
424
  // as a computed field (`never extends T` is always true, so same-named table
632
425
  // columns get omitted from select results), and an object type with a required
@@ -634,23 +427,15 @@ export const generateTypescript = async (
634
427
  // generated types. `Record<PropertyKey, never>` is the accurate type for
635
428
  // "callable with no arguments".
636
429
  const argsType =
637
- inArgs.length > 0
638
- ? getArgsTsType(schema, inArgs)
430
+ overload.args.length > 0
431
+ ? getArgsTsType(schema, overload.args)
639
432
  : "Record<PropertyKey, never>";
640
- let returnType = getFunctionReturnType(schema, fn);
433
+ const returnType =
434
+ overload.error === null
435
+ ? getFunctionReturnType(schema, overload)
436
+ : `{ error: true } & ${JSON.stringify(overload.error)}`;
641
437
 
642
- // Check for specific error cases
643
- const conflictError = getConflictError(schema, fns, fn, inArgs);
644
- if (conflictError) {
645
- returnType = `{ error: true } & ${JSON.stringify(conflictError)}`;
646
- } else if (hasTableRowError(fn, inArgs)) {
647
- // Special case for computed fields returning scalars functions
648
- returnType = `{ error: true } & ${JSON.stringify(
649
- `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`,
650
- )}`;
651
- }
652
-
653
- return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(fn, returnType)} }`;
438
+ return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(overload, returnType)} }`;
654
439
  })
655
440
  .join(" |\n");
656
441
  };
@@ -694,87 +479,37 @@ export const generateTypescript = async (
694
479
  }
695
480
 
696
481
  /**
697
- * Is `fn` a PostgREST computed field on `relation` (a table or view)?
698
- *
699
- * A computed field is a function taking a single argument of the relation's
700
- * own composite type, so it is selectable as if it were a column and belongs
701
- * in `Row`.
702
- *
703
- * Matching on the argument's type OID rather than on `argument_types` is what
704
- * makes named parameters work. `argument_types` is
705
- * `pg_get_function_arguments()`, which renders the parameter *name* when the
706
- * parameter has one — `category category` rather than `category` — and
707
- * schema-qualifies the type when it is not visible in `search_path`. Both
708
- * forms failed a bare string comparison against the relation name, so
709
- * `create function name_translated(category category)` silently lost its
710
- * computed field while the unnamed `name_translated(category)` kept it.
711
- * See supabase/postgres-meta#1034.
712
- */
713
- const isComputedFieldOf = (
714
- fn: PostgresFunction,
715
- inArgs: PostgresFunction["args"],
716
- relation: { id: number; name: string },
717
- ) => {
718
- const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
719
- if (soleInArg) {
720
- // `relationTypeByIds` is already "types backed by a relation", so a hit
721
- // identifies the relation regardless of how the argument was spelled.
722
- // A `variadic` argument is an array type and never appears here.
723
- const argType = relationTypeByIds.get(soleInArg.type_id);
724
- if (argType) {
725
- return argType.type_relation_id === relation.id;
726
- }
727
- }
728
- // Fall back to the historical comparison when the argument's type is not in
729
- // the metadata (callers may pass a reduced `types` list), so a computed
730
- // field that resolved before keeps resolving.
731
- return fn.argument_types === relation.name;
732
- };
733
-
734
- /**
735
- * The computed fields of a relation, in `schemaFunctions` order. A function
736
- * named like one of the relation's columns is left out: PostgREST resolves
737
- * the name to the column, so it is neither selectable nor part of `Row`.
482
+ * The computed fields of a relation, in function name order. A computed
483
+ * field is a function taking a single argument of the relation's own
484
+ * composite type, so it is selectable as if it were a column and belongs in
485
+ * `Row`; `resolveRpcFunctions` marks them with `computed_field_of`.
738
486
  */
739
487
  const computedFieldsOf = (
740
- schemaFunctions: {
741
- fn: PostgresFunction;
742
- inArgs: PostgresFunction["args"];
743
- }[],
744
- relation: { id: number; name: string },
745
- ) => {
746
- const columnNames = new Set(
747
- getColumns(relation.id).map((column) => column.name),
748
- );
749
- const seen = new Set<string>();
750
- return schemaFunctions.filter(({ fn, inArgs }) => {
751
- if (
752
- !isComputedFieldOf(fn, inArgs, relation) ||
753
- columnNames.has(fn.name) ||
754
- seen.has(fn.name)
755
- ) {
756
- return false;
757
- }
758
- seen.add(fn.name);
759
- return true;
488
+ schemaFunctions: RpcFunction[],
489
+ relation: { id: number },
490
+ ) =>
491
+ schemaFunctions.flatMap((fn) => {
492
+ const overload = fn.overloads.find(
493
+ ({ computed_field_of }) => computed_field_of === relation.id,
494
+ );
495
+ return overload ? [{ name: fn.name, overload }] : [];
760
496
  });
761
- };
762
497
 
763
498
  const computedFieldTsDefinition = (
764
499
  schema: PostgresSchema,
765
- { fn }: { fn: PostgresFunction },
500
+ { name, overload }: { name: string; overload: RpcFunctionOverload },
766
501
  ) =>
767
- `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(
768
- getFunctionReturnType(schema, fn),
502
+ `${JSON.stringify(name)}: ${generateNullableUnionTsType(
503
+ getFunctionReturnType(schema, overload),
769
504
  true,
770
505
  )}`;
771
506
 
772
507
  // `ComputedFields` names the `Row` keys PostgREST leaves out of `select('*')`,
773
508
  // so postgrest-js does not have to infer them from `Functions`.
774
- const computedFieldsTsType = (computedFields: { fn: PostgresFunction }[]) =>
509
+ const computedFieldsTsType = (computedFields: { name: string }[]) =>
775
510
  computedFields.length === 0
776
511
  ? "never"
777
- : computedFields.map(({ fn }) => JSON.stringify(fn.name)).join(" | ");
512
+ : computedFields.map(({ name }) => JSON.stringify(name)).join(" | ");
778
513
 
779
514
  let output = `
780
515
  export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
@@ -959,32 +694,16 @@ export type Database = {
959
694
  }
960
695
  }
961
696
  Functions: {
962
- ${(() => {
963
- if (schemaFunctions.length === 0) {
964
- return "[_ in never]: never";
965
- }
966
- const schemaFunctionsGroupedByName = schemaFunctions.reduce(
967
- (acc, curr) => {
968
- (acc[curr.fn.name] ??= []).push(curr);
969
- return acc;
970
- },
971
- {} as Record<string, typeof schemaFunctions>,
972
- );
973
- for (const fns of Object.values(schemaFunctionsGroupedByName)) {
974
- fns.sort(
975
- (a, b) =>
976
- compareStrings(a.fn.argument_types, b.fn.argument_types) ||
977
- compareStrings(a.fn.return_type, b.fn.return_type),
978
- );
979
- }
980
-
981
- return Object.entries(schemaFunctionsGroupedByName)
982
- .map(([fnName, fns]) => {
983
- const functionSignatures = getFunctionSignatures(schema, fns);
984
- return `${JSON.stringify(fnName)}:\n${functionSignatures}`;
985
- })
986
- .join(",\n");
987
- })()}
697
+ ${
698
+ schemaFunctions.length === 0
699
+ ? "[_ in never]: never"
700
+ : schemaFunctions
701
+ .map(
702
+ (fn) =>
703
+ `${JSON.stringify(fn.name)}:\n${getFunctionSignatures(schema, fn)}`,
704
+ )
705
+ .join(",\n")
706
+ }
988
707
  }
989
708
  Enums: {
990
709
  ${
@@ -1032,7 +751,7 @@ export type Database = {
1032
751
  type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
1033
752
 
1034
753
  type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(
1035
- defaultSchema,
754
+ resolvedDefaultSchema,
1036
755
  )}>]
1037
756
 
1038
757
  export type Tables<
package/src/index.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";