@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.
- package/README.md +154 -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 +72 -288
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/introspection/index.js +8 -3
- package/dist/introspection/node-tree.d.ts +18 -0
- package/dist/introspection/node-tree.js +100 -0
- package/dist/introspection/sql/view_definitions.sql.d.ts +8 -0
- package/dist/introspection/sql/view_definitions.sql.js +43 -0
- package/dist/introspection/view-nullability.d.ts +14 -0
- package/dist/introspection/view-nullability.js +167 -0
- package/dist/rpc.d.ts +2 -0
- package/dist/rpc.js +232 -0
- package/dist/sort.js +23 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.js +92 -0
- package/package.json +5 -4
- package/src/generation/index.ts +0 -5
- package/src/generation/typescript.ts +102 -383
- package/src/index.ts +1 -0
- package/src/introspection/index.ts +8 -2
- package/src/introspection/node-tree.ts +129 -0
- package/src/introspection/sql/view_definitions.sql.ts +45 -0
- package/src/introspection/view-nullability.ts +243 -0
- package/src/rpc.ts +307 -0
- package/src/sort.ts +31 -0
- package/src/types.ts +102 -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,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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
319
|
+
overload: RpcFunctionOverload,
|
|
374
320
|
returnType: string,
|
|
375
321
|
) => {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
346
|
+
{ returns }: RpcFunctionOverload,
|
|
440
347
|
): string => {
|
|
441
348
|
// 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
|
-
|
|
349
|
+
if (returns.columns) {
|
|
453
350
|
return `{
|
|
454
|
-
${
|
|
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
|
|
358
|
+
// Case 2: returns a row type of a relation in this schema.
|
|
459
359
|
const relation =
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
389
|
+
arg: RpcFunctionOverload["args"][number],
|
|
590
390
|
) => {
|
|
591
|
-
const
|
|
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
|
-
|
|
401
|
+
arg.format,
|
|
606
402
|
typeContext,
|
|
607
|
-
|
|
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
|
-
|
|
413
|
+
args: RpcFunctionOverload["args"],
|
|
618
414
|
) =>
|
|
619
|
-
`{ ${
|
|
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
|
-
|
|
626
|
-
|
|
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
|
-
|
|
638
|
-
? getArgsTsType(schema,
|
|
430
|
+
overload.args.length > 0
|
|
431
|
+
? getArgsTsType(schema, overload.args)
|
|
639
432
|
: "Record<PropertyKey, never>";
|
|
640
|
-
|
|
433
|
+
const returnType =
|
|
434
|
+
overload.error === null
|
|
435
|
+
? getFunctionReturnType(schema, overload)
|
|
436
|
+
: `{ error: true } & ${JSON.stringify(overload.error)}`;
|
|
641
437
|
|
|
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)} }`;
|
|
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
|
-
*
|
|
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`.
|
|
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
|
-
|
|
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;
|
|
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
|
-
{
|
|
500
|
+
{ name, overload }: { name: string; overload: RpcFunctionOverload },
|
|
766
501
|
) =>
|
|
767
|
-
`${JSON.stringify(
|
|
768
|
-
getFunctionReturnType(schema,
|
|
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: {
|
|
509
|
+
const computedFieldsTsType = (computedFields: { name: string }[]) =>
|
|
775
510
|
computedFields.length === 0
|
|
776
511
|
? "never"
|
|
777
|
-
: computedFields.map(({
|
|
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
|
-
|
|
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
|
-
})()}
|
|
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
|
-
|
|
754
|
+
resolvedDefaultSchema,
|
|
1036
755
|
)}>]
|
|
1037
756
|
|
|
1038
757
|
export type Tables<
|