@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.
@@ -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,20 @@ export const generateTypescript = async (
119
119
  materializedViews,
120
120
  columns,
121
121
  relationships,
122
- functions,
123
122
  types,
124
123
  } = metadata;
124
+ // Documents predating the collection (still valid version 1) resolve it here.
125
+ const rpcFunctions = metadata.rpcFunctions ?? resolveRpcFunctions(metadata);
125
126
  const {
126
127
  detectOneToOneRelationships = false,
127
128
  postgrestVersion,
128
129
  defaultSchema = "public",
129
130
  format = defaultFormat,
130
131
  } = opts;
132
+ const resolvedDefaultSchema =
133
+ schemas.some(({ name }) => name === defaultSchema) || !schemas[0]
134
+ ? defaultSchema
135
+ : schemas[0].name;
131
136
  // Every relation-typed value resolves against the same collections, so build
132
137
  // the lookup context once rather than reassembling it at each call site.
133
138
  const typeContext: TypeResolutionContext = {
@@ -149,7 +154,7 @@ export const generateTypescript = async (
149
154
  view: PostgresView;
150
155
  relationships: TsRelationship[];
151
156
  }[];
152
- functions: { fn: PostgresFunction; inArgs: PostgresFunction["args"] }[];
157
+ functions: RpcFunction[];
153
158
  enums: PostgresType[];
154
159
  compositeTypes: PostgresType[];
155
160
  }>(
@@ -159,8 +164,7 @@ export const generateTypescript = async (
159
164
  ]),
160
165
  );
161
166
  const columnsByTableId: Record<number, PostgresColumn[]> = {};
162
- const tablesNamesByTableId: Record<number, string> = {};
163
- const relationTypeByIds = new Map<number, (typeof types)[number]>();
167
+ const relationsById = new Map<number, { id: number; schema: string }>();
164
168
  // group types by id for quicker lookup
165
169
  const typesById = new Map<number, (typeof types)[number]>();
166
170
  const tablesLike = [
@@ -172,7 +176,7 @@ export const generateTypescript = async (
172
176
 
173
177
  for (const tableLike of tablesLike) {
174
178
  columnsByTableId[tableLike.id] = [];
175
- tablesNamesByTableId[tableLike.id] = tableLike.name;
179
+ relationsById.set(tableLike.id, tableLike);
176
180
  }
177
181
  for (const column of columns) {
178
182
  columnsByTableId[column.table_id]?.push(column);
@@ -198,10 +202,6 @@ export const generateTypescript = async (
198
202
 
199
203
  for (const type of types) {
200
204
  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
205
  if (type.enums.length > 0) {
206
206
  introspectionBySchema[type.schema]?.enums.push(type);
207
207
  }
@@ -288,66 +288,8 @@ export const generateTypescript = async (
288
288
  relationships: getRelationships(materializedView),
289
289
  });
290
290
  }
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
- }
291
+ for (const fn of rpcFunctions) {
292
+ introspectionBySchema[fn.schema]?.functions.push(fn);
351
293
  }
352
294
  // These per-schema groups MERGE multiple metadata collections — `.tables`
353
295
  // combines tables + foreign tables, `.views` combines views + materialized
@@ -360,110 +302,62 @@ export const generateTypescript = async (
360
302
  schemaIntrospection.views.sort((a, b) =>
361
303
  compareStrings(a.view.name, b.view.name),
362
304
  );
363
- schemaIntrospection.functions.sort((a, b) =>
364
- compareStrings(a.fn.name, b.fn.name),
365
- );
366
305
  schemaIntrospection.enums.sort((a, b) => compareStrings(a.name, b.name));
367
306
  schemaIntrospection.compositeTypes.sort((a, b) =>
368
307
  compareStrings(a.name, b.name),
369
308
  );
370
309
  }
371
310
 
311
+ const referencedTsType = (
312
+ schema: PostgresSchema,
313
+ { format, type_schema }: RpcTypeReference,
314
+ ) =>
315
+ format === null || type_schema === null
316
+ ? "unknown"
317
+ : pgTypeToTsType(schema, format, typeContext, type_schema);
318
+
372
319
  const getFunctionTsReturnType = (
373
- fn: PostgresFunction,
320
+ overload: RpcFunctionOverload,
374
321
  returnType: string,
375
322
  ) => {
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}` : ""}`;
323
+ const { setof_options } = overload;
324
+ const setofOptionsInfo = setof_options
325
+ ? `SetofOptions: {
326
+ from: ${JSON.stringify(setof_options.from)}
327
+ to: ${JSON.stringify(setof_options.to)}
328
+ isOneToOne: ${setof_options.is_one_to_one}
329
+ isSetofReturn: ${setof_options.is_setof_return}
330
+ }`
331
+ : "";
332
+ // A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted:
333
+ // postgrest-js relies on isOneToOne to restore the array shape for direct RPC calls.
334
+ // Without that metadata PostgREST still returns an array, so the collapse would make
335
+ // the type diverge from the actual response.
336
+ const collapsibleSingleRow = setof_options?.is_one_to_one === true;
337
+ return `${returnType}${overload.is_set_returning && !collapsibleSingleRow ? "[]" : ""}
338
+ ${setofOptionsInfo}`;
435
339
  };
436
340
 
437
341
  const getFunctionReturnType = (
438
342
  schema: PostgresSchema,
439
- fn: PostgresFunction,
343
+ { returns }: RpcFunctionOverload,
440
344
  ): string => {
441
345
  // 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
-
346
+ if (returns.columns) {
453
347
  return `{
454
- ${argsNameAndType.map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)}
348
+ ${returns.columns.map(
349
+ (column) =>
350
+ `${JSON.stringify(column.name)}: ${referencedTsType(schema, column)}`,
351
+ )}
455
352
  }`;
456
353
  }
457
354
 
458
- // Case 2: returns a relation's row type.
355
+ // Case 2: returns a row type of a relation in this schema.
459
356
  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) {
357
+ returns.relation_id === null
358
+ ? undefined
359
+ : relationsById.get(returns.relation_id);
360
+ if (relation && relation.schema === schema.name) {
467
361
  return `{
468
362
  ${getColumns(relation.id)
469
363
  .map((column) =>
@@ -484,127 +378,26 @@ export const generateTypescript = async (
484
378
  }
485
379
 
486
380
  // 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;
381
+ return referencedTsType(schema, returns);
585
382
  };
586
383
 
587
384
  const getArgTsType = (
588
385
  schema: PostgresSchema,
589
- { name, type_id }: PostgresFunction["args"][number],
386
+ arg: RpcFunctionOverload["args"][number],
590
387
  ) => {
591
- const type = typesById.get(type_id);
592
- if (!type) {
593
- return "unknown";
594
- }
595
- const tsType = pgTypeToTsType(schema, type.name, typeContext, type.schema);
388
+ const tsType = referencedTsType(schema, arg);
596
389
  // A row-typed parameter takes the relation's columns: PostgREST fills the
597
390
  // keys it does not receive with NULL, and the computed fields are never
598
391
  // input. A named parameter is typed without them. An unnamed one keeps the
599
392
  // plain `Row` reference, which is how postgrest-js versions predating
600
393
  // `ComputedFields` recognise the function as a computed field.
601
- if (name === "") {
394
+ if (arg.name === "" || arg.format === null || arg.type_schema === null) {
602
395
  return tsType;
603
396
  }
604
397
  const relationReference = findRelationReference(
605
- type.name,
398
+ arg.format,
606
399
  typeContext,
607
- type.schema,
400
+ arg.type_schema,
608
401
  );
609
402
  if (relationReference && tsType === `${relationReference}['Row']`) {
610
403
  return `Omit<${tsType}, ${relationReference}['ComputedFields']>`;
@@ -614,19 +407,16 @@ export const generateTypescript = async (
614
407
 
615
408
  const getArgsTsType = (
616
409
  schema: PostgresSchema,
617
- inArgs: PostgresFunction["args"],
410
+ args: RpcFunctionOverload["args"],
618
411
  ) =>
619
- `{ ${inArgs.map(
412
+ `{ ${args.map(
620
413
  (arg) =>
621
414
  `${JSON.stringify(arg.name)}${arg.has_default ? "?" : ""}: ${getArgTsType(schema, arg)}`,
622
415
  )} }`;
623
416
 
624
- const getFunctionSignatures = (
625
- schema: PostgresSchema,
626
- fns: Array<{ fn: PostgresFunction; inArgs: PostgresFunction["args"] }>,
627
- ) => {
628
- return fns
629
- .map(({ fn, inArgs }) => {
417
+ const getFunctionSignatures = (schema: PostgresSchema, fn: RpcFunction) => {
418
+ return fn.overloads
419
+ .map((overload) => {
630
420
  // `never` would be wrong here: postgrest-js treats `Args extends { '': Row }`
631
421
  // as a computed field (`never extends T` is always true, so same-named table
632
422
  // columns get omitted from select results), and an object type with a required
@@ -634,23 +424,15 @@ export const generateTypescript = async (
634
424
  // generated types. `Record<PropertyKey, never>` is the accurate type for
635
425
  // "callable with no arguments".
636
426
  const argsType =
637
- inArgs.length > 0
638
- ? getArgsTsType(schema, inArgs)
427
+ overload.args.length > 0
428
+ ? getArgsTsType(schema, overload.args)
639
429
  : "Record<PropertyKey, never>";
640
- let returnType = getFunctionReturnType(schema, fn);
430
+ const returnType =
431
+ overload.error === null
432
+ ? getFunctionReturnType(schema, overload)
433
+ : `{ error: true } & ${JSON.stringify(overload.error)}`;
641
434
 
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)} }`;
435
+ return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(overload, returnType)} }`;
654
436
  })
655
437
  .join(" |\n");
656
438
  };
@@ -694,87 +476,37 @@ export const generateTypescript = async (
694
476
  }
695
477
 
696
478
  /**
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`.
479
+ * The computed fields of a relation, in function name order. A computed
480
+ * field is a function taking a single argument of the relation's own
481
+ * composite type, so it is selectable as if it were a column and belongs in
482
+ * `Row`; `resolveRpcFunctions` marks them with `computed_field_of`.
738
483
  */
739
484
  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;
485
+ schemaFunctions: RpcFunction[],
486
+ relation: { id: number },
487
+ ) =>
488
+ schemaFunctions.flatMap((fn) => {
489
+ const overload = fn.overloads.find(
490
+ ({ computed_field_of }) => computed_field_of === relation.id,
491
+ );
492
+ return overload ? [{ name: fn.name, overload }] : [];
760
493
  });
761
- };
762
494
 
763
495
  const computedFieldTsDefinition = (
764
496
  schema: PostgresSchema,
765
- { fn }: { fn: PostgresFunction },
497
+ { name, overload }: { name: string; overload: RpcFunctionOverload },
766
498
  ) =>
767
- `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(
768
- getFunctionReturnType(schema, fn),
499
+ `${JSON.stringify(name)}: ${generateNullableUnionTsType(
500
+ getFunctionReturnType(schema, overload),
769
501
  true,
770
502
  )}`;
771
503
 
772
504
  // `ComputedFields` names the `Row` keys PostgREST leaves out of `select('*')`,
773
505
  // so postgrest-js does not have to infer them from `Functions`.
774
- const computedFieldsTsType = (computedFields: { fn: PostgresFunction }[]) =>
506
+ const computedFieldsTsType = (computedFields: { name: string }[]) =>
775
507
  computedFields.length === 0
776
508
  ? "never"
777
- : computedFields.map(({ fn }) => JSON.stringify(fn.name)).join(" | ");
509
+ : computedFields.map(({ name }) => JSON.stringify(name)).join(" | ");
778
510
 
779
511
  let output = `
780
512
  export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
@@ -959,32 +691,16 @@ export type Database = {
959
691
  }
960
692
  }
961
693
  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
- })()}
694
+ ${
695
+ schemaFunctions.length === 0
696
+ ? "[_ in never]: never"
697
+ : schemaFunctions
698
+ .map(
699
+ (fn) =>
700
+ `${JSON.stringify(fn.name)}:\n${getFunctionSignatures(schema, fn)}`,
701
+ )
702
+ .join(",\n")
703
+ }
988
704
  }
989
705
  Enums: {
990
706
  ${
@@ -1032,7 +748,7 @@ export type Database = {
1032
748
  type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
1033
749
 
1034
750
  type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(
1035
- defaultSchema,
751
+ resolvedDefaultSchema,
1036
752
  )}>]
1037
753
 
1038
754
  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";