@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.
- package/README.md +120 -21
- package/dist/generation/index.d.ts +0 -1
- package/dist/generation/index.js +0 -1
- package/dist/generation/typescript.d.ts +2 -1
- package/dist/generation/typescript.js +70 -288
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/introspection/index.js +3 -1
- package/dist/rpc.d.ts +2 -0
- package/dist/rpc.js +223 -0
- package/dist/sort.js +23 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.js +91 -0
- package/package.json +5 -4
- package/src/generation/index.ts +0 -5
- package/src/generation/typescript.ts +99 -383
- package/src/index.ts +1 -0
- package/src/introspection/index.ts +3 -1
- package/src/rpc.ts +299 -0
- package/src/sort.ts +31 -0
- package/src/types.ts +101 -0
- package/dist/generation/constants.d.ts +0 -9
- package/dist/generation/constants.js +0 -10
- package/dist/generation/swift.d.ts +0 -11
- package/dist/generation/swift.js +0 -358
- package/src/generation/constants.ts +0 -11
- package/src/generation/swift.ts +0 -531
|
@@ -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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
292
|
-
|
|
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
|
-
|
|
320
|
+
overload: RpcFunctionOverload,
|
|
374
321
|
returnType: string,
|
|
375
322
|
) => {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
343
|
+
{ returns }: RpcFunctionOverload,
|
|
440
344
|
): string => {
|
|
441
345
|
// Case 1: `returns table`.
|
|
442
|
-
|
|
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
|
-
${
|
|
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
|
|
355
|
+
// Case 2: returns a row type of a relation in this schema.
|
|
459
356
|
const relation =
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
386
|
+
arg: RpcFunctionOverload["args"][number],
|
|
590
387
|
) => {
|
|
591
|
-
const
|
|
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
|
-
|
|
398
|
+
arg.format,
|
|
606
399
|
typeContext,
|
|
607
|
-
|
|
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
|
-
|
|
410
|
+
args: RpcFunctionOverload["args"],
|
|
618
411
|
) =>
|
|
619
|
-
`{ ${
|
|
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
|
-
|
|
626
|
-
|
|
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
|
-
|
|
638
|
-
? getArgsTsType(schema,
|
|
427
|
+
overload.args.length > 0
|
|
428
|
+
? getArgsTsType(schema, overload.args)
|
|
639
429
|
: "Record<PropertyKey, never>";
|
|
640
|
-
|
|
430
|
+
const returnType =
|
|
431
|
+
overload.error === null
|
|
432
|
+
? getFunctionReturnType(schema, overload)
|
|
433
|
+
: `{ error: true } & ${JSON.stringify(overload.error)}`;
|
|
641
434
|
|
|
642
|
-
|
|
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
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
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
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
-
{
|
|
497
|
+
{ name, overload }: { name: string; overload: RpcFunctionOverload },
|
|
766
498
|
) =>
|
|
767
|
-
`${JSON.stringify(
|
|
768
|
-
getFunctionReturnType(schema,
|
|
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: {
|
|
506
|
+
const computedFieldsTsType = (computedFields: { name: string }[]) =>
|
|
775
507
|
computedFields.length === 0
|
|
776
508
|
? "never"
|
|
777
|
-
: computedFields.map(({
|
|
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
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
751
|
+
resolvedDefaultSchema,
|
|
1036
752
|
)}>]
|
|
1037
753
|
|
|
1038
754
|
export type Tables<
|