@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.
@@ -0,0 +1,8 @@
1
+ import type { SchemaFilterProps } from "./common.ts";
2
+ /**
3
+ * The query tree of every view and materialized view in the filtered schemas,
4
+ * plus every view they read from in any schema, with the `NOT NULL` columns
5
+ * each one references. `inferNonNullViewColumns` resolves view column
6
+ * nullability from it.
7
+ */
8
+ export declare const VIEW_DEFINITIONS_SQL: (props: SchemaFilterProps) => string;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The query tree of every view and materialized view in the filtered schemas,
3
+ * plus every view they read from in any schema, with the `NOT NULL` columns
4
+ * each one references. `inferNonNullViewColumns` resolves view column
5
+ * nullability from it.
6
+ */
7
+ export const VIEW_DEFINITIONS_SQL = (props) => /* SQL */ `
8
+ WITH RECURSIVE view_closure(oid) AS (
9
+ SELECT c.oid
10
+ FROM pg_class c
11
+ JOIN pg_namespace n ON n.oid = c.relnamespace
12
+ WHERE
13
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
14
+ c.relkind IN ('v', 'm')
15
+ UNION
16
+ SELECT dep.refobjid
17
+ FROM view_closure vc
18
+ JOIN pg_rewrite r ON r.ev_class = vc.oid AND r.rulename = '_RETURN'
19
+ JOIN pg_depend dep ON dep.classid = 'pg_rewrite' :: regclass
20
+ AND dep.objid = r.oid
21
+ AND dep.refclassid = 'pg_class' :: regclass
22
+ JOIN pg_class rc ON rc.oid = dep.refobjid AND rc.relkind IN ('v', 'm')
23
+ )
24
+ SELECT
25
+ r.ev_class :: int8 AS view_id,
26
+ r.ev_action :: text AS definition,
27
+ COALESCE(
28
+ (
29
+ SELECT json_agg(json_build_array(dep.refobjid :: int8, dep.refobjsubid))
30
+ FROM pg_depend dep
31
+ JOIN pg_attribute a ON a.attrelid = dep.refobjid AND a.attnum = dep.refobjsubid
32
+ JOIN pg_type t ON t.oid = a.atttypid
33
+ WHERE dep.classid = 'pg_rewrite' :: regclass
34
+ AND dep.objid = r.oid
35
+ AND dep.refclassid = 'pg_class' :: regclass
36
+ AND dep.refobjsubid > 0
37
+ AND (a.attnotnull OR t.typtype = 'd' AND t.typnotnull)
38
+ ),
39
+ '[]'
40
+ ) AS non_null_columns
41
+ FROM view_closure vc
42
+ JOIN pg_rewrite r ON r.ev_class = vc.oid AND r.rulename = '_RETURN'
43
+ `;
@@ -0,0 +1,14 @@
1
+ import type { PostgresColumn } from "../types.ts";
2
+ /** A row of `VIEW_DEFINITIONS_SQL`. */
3
+ export interface ViewDefinitionRow {
4
+ view_id: number | string;
5
+ definition: string;
6
+ non_null_columns: [number | string, number | string][];
7
+ }
8
+ /**
9
+ * Mark view and materialized view columns as not nullable when PostgreSQL can
10
+ * prove it from the view's query tree (see {@link inferNonNullViewColumns}) or
11
+ * when the column comment has an `@notNull` line. PostgreSQL never sets
12
+ * `attnotnull` on view columns, so without this every one of them is nullable.
13
+ */
14
+ export declare function applyViewColumnNullability<T extends PostgresColumn>(columns: T[], rows: ViewDefinitionRow[]): T[];
@@ -0,0 +1,167 @@
1
+ import { isNode, parseNodeTree, } from "./node-tree.js";
2
+ const RTE_RELATION = "0";
3
+ const RTE_SUBQUERY = "1";
4
+ const RTE_JOIN = "2";
5
+ const RTE_CTE = "6";
6
+ const RTE_GROUP = "9";
7
+ const JOIN_INNER = "0";
8
+ const JOIN_LEFT = "1";
9
+ const JOIN_RIGHT = "3";
10
+ const NOT_NULL_DIRECTIVE = /^\s*@notNull\s*$/m;
11
+ /**
12
+ * Mark view and materialized view columns as not nullable when PostgreSQL can
13
+ * prove it from the view's query tree (see {@link inferNonNullViewColumns}) or
14
+ * when the column comment has an `@notNull` line. PostgreSQL never sets
15
+ * `attnotnull` on view columns, so without this every one of them is nullable.
16
+ */
17
+ export function applyViewColumnNullability(columns, rows) {
18
+ const viewIds = new Set(rows.map((row) => String(row.view_id)));
19
+ const nonNull = inferNonNullViewColumns(rows);
20
+ return columns.map((column) => column.is_nullable &&
21
+ viewIds.has(String(column.table_id)) &&
22
+ (nonNull.has(column.id) || NOT_NULL_DIRECTIVE.test(column.comment ?? ""))
23
+ ? { ...column, is_nullable: false }
24
+ : column);
25
+ }
26
+ /**
27
+ * The ids (`<view oid>.<attnum>`) of the view columns that can never be null.
28
+ * A column qualifies when it is a plain reference to a `NOT NULL` column, or
29
+ * to a view column that qualifies, followed through joins, subqueries,
30
+ * non-recursive CTEs and `GROUP BY`, and nothing on the way puts it on the
31
+ * nullable side of an outer join, in a set operation or under grouping sets.
32
+ * The outer-join check walks the join tree instead of reading
33
+ * `varnullingrels`, which PostgreSQL 15 does not have.
34
+ */
35
+ function inferNonNullViewColumns(rows) {
36
+ const definitions = new Map(rows.map((row) => [String(row.view_id), row.definition]));
37
+ const nonNullBaseColumns = new Set(rows.flatMap((row) => row.non_null_columns.map(([relation, attnum]) => `${relation}.${attnum}`)));
38
+ const resolved = new Map();
39
+ const nullableIndexesByQuery = new Map();
40
+ const nullableIndexesOf = (query) => {
41
+ let indexes = nullableIndexesByQuery.get(query);
42
+ if (indexes === undefined) {
43
+ indexes = nullableRangeTableIndexes(query);
44
+ nullableIndexesByQuery.set(query, indexes);
45
+ }
46
+ return indexes;
47
+ };
48
+ const nonNullColumnsOfView = (viewId) => {
49
+ const cached = resolved.get(viewId);
50
+ if (cached !== undefined) {
51
+ return cached;
52
+ }
53
+ const columns = new Set();
54
+ resolved.set(viewId, columns);
55
+ try {
56
+ const tree = parseNodeTree(definitions.get(viewId) ?? "");
57
+ const query = Array.isArray(tree) ? tree[0] : tree;
58
+ if (isNode(query, "QUERY") && allowsNonNull(query)) {
59
+ for (const entry of list(query.fields.targetList)) {
60
+ if (isNode(entry, "TARGETENTRY") &&
61
+ entry.fields.resjunk !== "true" &&
62
+ isNonNullExpression(entry.fields.expr, [query])) {
63
+ columns.add(Number(entry.fields.resno));
64
+ }
65
+ }
66
+ }
67
+ }
68
+ catch {
69
+ columns.clear();
70
+ }
71
+ return columns;
72
+ };
73
+ const isNonNullColumn = (relation, attnum) => definitions.has(relation)
74
+ ? nonNullColumnsOfView(relation).has(attnum)
75
+ : nonNullBaseColumns.has(`${relation}.${attnum}`);
76
+ const isNonNullExpression = (expression, queries) => {
77
+ const query = queries.at(-1);
78
+ if (query === undefined ||
79
+ !isNode(expression, "VAR") ||
80
+ expression.fields.varlevelsup !== "0") {
81
+ return false;
82
+ }
83
+ const varno = Number(expression.fields.varno);
84
+ const attnum = Number(expression.fields.varattno);
85
+ if (attnum <= 0 || nullableIndexesOf(query).has(varno)) {
86
+ return false;
87
+ }
88
+ const entry = list(query.fields.rtable)[varno - 1];
89
+ if (!isNode(entry, "RANGETBLENTRY")) {
90
+ return false;
91
+ }
92
+ switch (entry.fields.rtekind) {
93
+ case RTE_RELATION:
94
+ return (typeof entry.fields.relid === "string" &&
95
+ isNonNullColumn(entry.fields.relid, attnum));
96
+ case RTE_SUBQUERY:
97
+ return isNonNullOutput(entry.fields.subquery, attnum, queries);
98
+ case RTE_JOIN:
99
+ return isNonNullExpression(list(entry.fields.joinaliasvars)[attnum - 1], queries);
100
+ case RTE_GROUP:
101
+ return isNonNullExpression(list(entry.fields.groupexprs)[attnum - 1], queries);
102
+ case RTE_CTE: {
103
+ const levelsUp = Number(entry.fields.ctelevelsup);
104
+ const owner = queries.at(-1 - levelsUp);
105
+ const cte = list(owner?.fields.cteList).find((candidate) => isNode(candidate, "COMMONTABLEEXPR") &&
106
+ candidate.fields.ctename === entry.fields.ctename);
107
+ return (isNode(cte) &&
108
+ cte.fields.cterecursive !== "true" &&
109
+ isNonNullOutput(cte.fields.ctequery, attnum, queries.slice(0, queries.length - levelsUp)));
110
+ }
111
+ default:
112
+ return false;
113
+ }
114
+ };
115
+ const isNonNullOutput = (subquery, resno, queries) => {
116
+ if (!isNode(subquery, "QUERY") || !allowsNonNull(subquery)) {
117
+ return false;
118
+ }
119
+ const entry = list(subquery.fields.targetList).find((candidate) => isNode(candidate, "TARGETENTRY") &&
120
+ Number(candidate.fields.resno) === resno);
121
+ return (isNode(entry) &&
122
+ isNonNullExpression(entry.fields.expr, [...queries, subquery]));
123
+ };
124
+ const result = new Set();
125
+ for (const viewId of definitions.keys()) {
126
+ for (const attnum of nonNullColumnsOfView(viewId)) {
127
+ result.add(`${viewId}.${attnum}`);
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+ function allowsNonNull(query) {
133
+ return (query.fields.setOperations === null &&
134
+ list(query.fields.groupingSets).length === 0);
135
+ }
136
+ /**
137
+ * The range table indexes that sit on the nullable side of an outer join in
138
+ * the query's join tree, including the indexes of nested joins themselves.
139
+ */
140
+ function nullableRangeTableIndexes(query) {
141
+ const nullable = new Set();
142
+ const visit = (node, isNullable) => {
143
+ if (isNode(node, "RANGETBLREF")) {
144
+ if (isNullable) {
145
+ nullable.add(Number(node.fields.rtindex));
146
+ }
147
+ }
148
+ else if (isNode(node, "FROMEXPR")) {
149
+ for (const item of list(node.fields.fromlist)) {
150
+ visit(item, isNullable);
151
+ }
152
+ }
153
+ else if (isNode(node, "JOINEXPR")) {
154
+ const joinType = node.fields.jointype;
155
+ if (isNullable) {
156
+ nullable.add(Number(node.fields.rtindex));
157
+ }
158
+ visit(node.fields.larg, isNullable || (joinType !== JOIN_INNER && joinType !== JOIN_LEFT));
159
+ visit(node.fields.rarg, isNullable || (joinType !== JOIN_INNER && joinType !== JOIN_RIGHT));
160
+ }
161
+ };
162
+ visit(query.fields.jointree, false);
163
+ return nullable;
164
+ }
165
+ function list(value) {
166
+ return Array.isArray(value) ? value : [];
167
+ }
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { GeneratorMetadata, RpcFunction } from "./types.ts";
2
+ export declare function resolveRpcFunctions(metadata: GeneratorMetadata): RpcFunction[];
package/dist/rpc.js ADDED
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Resolves the PostgREST view of `GeneratorMetadata.functions`.
3
+ *
4
+ * The raw `functions` collection is `pg_proc` as introspected. Which of those
5
+ * functions PostgREST exposes under `/rpc`, how their overloads group, what a
6
+ * call returns and whether a function doubles as a computed field or an
7
+ * embeddable relation are PostgREST rules, not language rules. This pass
8
+ * applies them once so every generator, in this package or in another SDK
9
+ * repo reading the serialized document, emits the same RPC surface.
10
+ *
11
+ * Pure and deterministic: the output is canonically ordered regardless of the
12
+ * order of the input collections. `introspect()` attaches the result as
13
+ * `rpcFunctions`; generators that meet a document without it (produced by a
14
+ * version predating the collection, or by a custom producer) run this pass
15
+ * themselves.
16
+ */
17
+ import { compareStrings } from "./collation.js";
18
+ /** json, jsonb and text: the types PostgREST accepts for a single unnamed parameter. */
19
+ const UNNAMED_ARG_TYPE_IDS = new Set([114, 3802, 25]);
20
+ const INPUT_ARG_MODES = new Set(["in", "inout", "variadic"]);
21
+ /** A NUL separator cannot occur in an identifier, so names with dots never collide. */
22
+ const qualifiedKey = (schema, name) => `${schema}\0${name}`;
23
+ export function resolveRpcFunctions(metadata) {
24
+ const schemaNames = new Set(metadata.schemas.map(({ name }) => name));
25
+ const typesById = new Map(metadata.types.map((type) => [type.id, type]));
26
+ const relationTypesById = new Map(metadata.types
27
+ .filter((type) => type.type_relation_id !== null)
28
+ .map((type) => [type.id, type]));
29
+ const relations = [
30
+ ...metadata.tables,
31
+ ...metadata.foreignTables,
32
+ ...metadata.views,
33
+ ...metadata.materializedViews,
34
+ ];
35
+ const relationsById = new Map(relations.map((relation) => [relation.id, relation]));
36
+ const relationsBySchemaName = new Map(relations.map((relation) => [
37
+ qualifiedKey(relation.schema, relation.name),
38
+ relation,
39
+ ]));
40
+ const columnNamesByRelationId = new Map();
41
+ for (const column of metadata.columns) {
42
+ let names = columnNamesByRelationId.get(column.table_id);
43
+ if (!names) {
44
+ names = new Set();
45
+ columnNamesByRelationId.set(column.table_id, names);
46
+ }
47
+ names.add(column.name);
48
+ }
49
+ const typeReference = (typeId) => {
50
+ const type = typesById.get(typeId);
51
+ return {
52
+ type_id: typeId,
53
+ format: type?.name ?? null,
54
+ type_schema: type?.schema ?? null,
55
+ };
56
+ };
57
+ const typeName = (typeId) => typesById.get(typeId)?.name ?? "unknown";
58
+ // The relation a return type stands for, or a composite type's name when the
59
+ // return type is one, since both are relations in `pg_class`. Resolved
60
+ // against every schema: a function may return rows of another schema.
61
+ const returnRelationName = (fn) => {
62
+ if (fn.return_type_relation_id === null)
63
+ return null;
64
+ return (relationsById.get(fn.return_type_relation_id)?.name ??
65
+ relationTypesById.get(fn.return_type_id)?.name ??
66
+ null);
67
+ };
68
+ // The function is a computed field of the relation whose row type its sole
69
+ // input parameter has, provided the relation lives in the function's schema
70
+ // and has no column of the function's name: PostgREST resolves the name to
71
+ // the column, so such a function is neither selectable nor part of `Row`.
72
+ // When the parameter's type is not in `types` (a reduced custom document),
73
+ // the rendered argument list may still name the relation.
74
+ const computedFieldOf = ({ fn, inArgs }) => {
75
+ const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
76
+ if (!soleInArg)
77
+ return null;
78
+ const argType = relationTypesById.get(soleInArg.type_id);
79
+ const relation = argType
80
+ ? relationsById.get(argType.type_relation_id)
81
+ : typesById.has(soleInArg.type_id)
82
+ ? undefined
83
+ : relationsBySchemaName.get(qualifiedKey(fn.schema, fn.argument_types));
84
+ if (!relation || relation.schema !== fn.schema)
85
+ return null;
86
+ if (columnNamesByRelationId.get(relation.id)?.has(fn.name))
87
+ return null;
88
+ return relation.id;
89
+ };
90
+ const isCallable = ({ inArgs }) => {
91
+ if (inArgs.length === 0)
92
+ return true;
93
+ if (!inArgs.some(({ name }) => name === ""))
94
+ return true;
95
+ if (inArgs.every((arg) => arg.name !== "" ||
96
+ (arg.has_default === true && UNNAMED_ARG_TYPE_IDS.has(arg.type_id)))) {
97
+ return true;
98
+ }
99
+ const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
100
+ return (soleInArg !== undefined &&
101
+ soleInArg.name === "" &&
102
+ (UNNAMED_ARG_TYPE_IDS.has(soleInArg.type_id) ||
103
+ relationTypesById.has(soleInArg.type_id)));
104
+ };
105
+ const overloadError = (candidate, group) => {
106
+ const { fn, inArgs } = candidate;
107
+ const soleInArg = inArgs.length === 1 ? inArgs[0] : undefined;
108
+ if (group.length > 1) {
109
+ // A no-argument overload next to one whose sole unnamed parameter has a
110
+ // default: PostgREST cannot pick one for an empty call.
111
+ if (inArgs.length === 0) {
112
+ const conflicting = group.find((other) => other !== candidate &&
113
+ other.inArgs.length === 1 &&
114
+ other.inArgs[0]?.name === "" &&
115
+ other.inArgs[0].has_default === true);
116
+ if (conflicting) {
117
+ return `Could not choose the best candidate function between: ${fn.schema}.${fn.name}(), ${fn.schema}.${fn.name}( => ${typeName(conflicting.fn.return_type_id)}). Try renaming the parameters or the function itself in the database so function overloading can be resolved`;
118
+ }
119
+ }
120
+ // Overloads whose sole parameter shares a name but not a type: a call
121
+ // carries no type information, so PostgREST cannot pick one.
122
+ if (soleInArg && soleInArg.name !== "") {
123
+ const conflicting = group.filter((other) => other !== candidate &&
124
+ other.inArgs.length === 1 &&
125
+ other.inArgs[0]?.name === soleInArg.name &&
126
+ other.inArgs[0].type_id !== soleInArg.type_id);
127
+ if (conflicting.length > 0) {
128
+ const conflictList = [candidate, ...conflicting]
129
+ .sort((a, b) => (a.inArgs[0]?.type_id || 0) - (b.inArgs[0]?.type_id || 0))
130
+ .map((other) => `${fn.schema}.${fn.name}(${other.inArgs
131
+ .map((arg) => `${arg.name} => ${typeName(arg.type_id)}`)
132
+ .join(", ")})`)
133
+ .join(", ");
134
+ 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`;
135
+ }
136
+ }
137
+ }
138
+ // PostgREST passes a sole unnamed parameter only when it is json, jsonb or
139
+ // text, so a function taking a row this way is a computed field or a
140
+ // computed relationship, reachable by embedding it from its relation and
141
+ // never through `/rpc`, whatever it returns.
142
+ if (soleInArg &&
143
+ soleInArg.name === "" &&
144
+ relationTypesById.has(soleInArg.type_id)) {
145
+ return `the function ${fn.schema}.${fn.name} with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache`;
146
+ }
147
+ return null;
148
+ };
149
+ const resolveOverload = (candidate, group) => {
150
+ const { fn, inArgs } = candidate;
151
+ const args = inArgs
152
+ .map((arg) => {
153
+ const relationId = relationTypesById.get(arg.type_id)?.type_relation_id;
154
+ return {
155
+ name: arg.name,
156
+ mode: arg.mode,
157
+ ...typeReference(arg.type_id),
158
+ has_default: arg.has_default === true,
159
+ relation_id: relationId !== undefined && relationsById.has(relationId)
160
+ ? relationId
161
+ : null,
162
+ };
163
+ })
164
+ .sort((a, b) => compareStrings(a.name, b.name));
165
+ const tableColumns = fn.args
166
+ .filter(({ mode }) => mode === "table")
167
+ .map(({ name, type_id }) => ({ name, ...typeReference(type_id) }))
168
+ .sort((a, b) => compareStrings(a.name, b.name));
169
+ const relationName = returnRelationName(fn);
170
+ const returnsMultipleRows = fn.prorows !== null && fn.prorows > 1;
171
+ // A function whose sole parameter (of any mode) is a row type is embedded
172
+ // from that relation; any other row-returning function embeds from `*`.
173
+ const soleArg = fn.args.length === 1 ? fn.args[0] : undefined;
174
+ const sourceType = soleArg && relationTypesById.get(soleArg.type_id);
175
+ const setofOptions = relationName === null
176
+ ? null
177
+ : {
178
+ from: sourceType ? sourceType.format : "*",
179
+ to: relationName,
180
+ is_one_to_one: sourceType && !fn.is_set_returning_function
181
+ ? true
182
+ : !returnsMultipleRows,
183
+ is_setof_return: fn.is_set_returning_function,
184
+ };
185
+ return {
186
+ function_id: fn.id,
187
+ args,
188
+ returns: {
189
+ ...typeReference(fn.return_type_id),
190
+ relation_id: fn.return_type_relation_id !== null &&
191
+ relationsById.has(fn.return_type_relation_id)
192
+ ? fn.return_type_relation_id
193
+ : null,
194
+ columns: tableColumns.length > 0 ? tableColumns : null,
195
+ },
196
+ is_set_returning: fn.is_set_returning_function,
197
+ rows_estimate: fn.prorows,
198
+ setof_options: setofOptions,
199
+ computed_field_of: computedFieldOf(candidate),
200
+ error: overloadError(candidate, group),
201
+ };
202
+ };
203
+ const groups = new Map();
204
+ for (const fn of metadata.functions) {
205
+ if (!schemaNames.has(fn.schema))
206
+ continue;
207
+ const inArgs = fn.args.filter((arg) => INPUT_ARG_MODES.has(arg.mode));
208
+ const candidate = { fn, inArgs };
209
+ if (!isCallable(candidate))
210
+ continue;
211
+ const key = qualifiedKey(fn.schema, fn.name);
212
+ let group = groups.get(key);
213
+ if (!group) {
214
+ group = [];
215
+ groups.set(key, group);
216
+ }
217
+ group.push(candidate);
218
+ }
219
+ return [...groups.values()]
220
+ .map((group) => {
221
+ const sorted = [...group].sort((a, b) => compareStrings(a.fn.argument_types, b.fn.argument_types) ||
222
+ compareStrings(a.fn.return_type, b.fn.return_type) ||
223
+ a.fn.id - b.fn.id);
224
+ const [{ fn }] = sorted;
225
+ return {
226
+ schema: fn.schema,
227
+ name: fn.name,
228
+ overloads: sorted.map((candidate) => resolveOverload(candidate, sorted)),
229
+ };
230
+ })
231
+ .sort((a, b) => compareStrings(a.schema, b.schema) || compareStrings(a.name, b.name));
232
+ }
package/dist/sort.js CHANGED
@@ -60,5 +60,28 @@ export function sortGeneratorMetadata(metadata) {
60
60
  args: [...fn.args].sort((a, b) => compareStrings(a.name, b.name)),
61
61
  })),
62
62
  types: [...metadata.types].sort(bySchemaName),
63
+ // `resolveRpcFunctions` orders the overloads of a function by signature,
64
+ // and that order is kept here, like composite attributes; the entries,
65
+ // their args and their table columns are addressed by name.
66
+ ...(metadata.rpcFunctions === undefined
67
+ ? {}
68
+ : {
69
+ rpcFunctions: [...metadata.rpcFunctions]
70
+ .sort((a, b) => compareStrings(a.schema, b.schema) ||
71
+ compareStrings(a.name, b.name))
72
+ .map((fn) => ({
73
+ ...fn,
74
+ overloads: fn.overloads.map((overload) => ({
75
+ ...overload,
76
+ args: [...overload.args].sort((a, b) => compareStrings(a.name, b.name)),
77
+ returns: {
78
+ ...overload.returns,
79
+ columns: overload.returns.columns === null
80
+ ? null
81
+ : [...overload.returns.columns].sort((a, b) => compareStrings(a.name, b.name)),
82
+ },
83
+ })),
84
+ })),
85
+ }),
63
86
  };
64
87
  }
package/dist/types.d.ts CHANGED
@@ -208,6 +208,119 @@ declare const postgresViewSchema: import("arktype/internal/variants/object.ts").
208
208
  }[] | undefined;
209
209
  }, {}>;
210
210
  export type PostgresView = typeof postgresViewSchema.infer;
211
+ /**
212
+ * A reference to a `types` entry by oid. `format` is the type's `name` (the
213
+ * same spelling `PostgresColumn.format` uses, `_` prefixed for arrays) and
214
+ * `type_schema` its schema; both are `null` when the type is not in `types`,
215
+ * which never happens in a document produced by `introspect()`.
216
+ */
217
+ declare const rpcTypeReferenceSchema: import("arktype/internal/variants/object.ts").ObjectType<{
218
+ type_id: number;
219
+ format: string | null;
220
+ type_schema: string | null;
221
+ }, {}>;
222
+ export type RpcTypeReference = typeof rpcTypeReferenceSchema.infer;
223
+ declare const rpcFunctionArgSchema: import("arktype/internal/variants/object.ts").ObjectType<{
224
+ type_id: number;
225
+ format: string | null;
226
+ type_schema: string | null;
227
+ name: string;
228
+ mode: "in" | "inout" | "variadic";
229
+ has_default: boolean;
230
+ relation_id: number | null;
231
+ }, {}>;
232
+ export type RpcFunctionArg = typeof rpcFunctionArgSchema.infer;
233
+ declare const rpcFunctionReturnsSchema: import("arktype/internal/variants/object.ts").ObjectType<{
234
+ type_id: number;
235
+ format: string | null;
236
+ type_schema: string | null;
237
+ relation_id: number | null;
238
+ columns: {
239
+ type_id: number;
240
+ format: string | null;
241
+ type_schema: string | null;
242
+ name: string;
243
+ }[] | null;
244
+ }, {}>;
245
+ export type RpcFunctionReturns = typeof rpcFunctionReturnsSchema.infer;
246
+ declare const rpcFunctionOverloadSchema: import("arktype/internal/variants/object.ts").ObjectType<{
247
+ function_id: number;
248
+ args: {
249
+ type_id: number;
250
+ format: string | null;
251
+ type_schema: string | null;
252
+ name: string;
253
+ mode: "in" | "inout" | "variadic";
254
+ has_default: boolean;
255
+ relation_id: number | null;
256
+ }[];
257
+ returns: {
258
+ type_id: number;
259
+ format: string | null;
260
+ type_schema: string | null;
261
+ relation_id: number | null;
262
+ columns: {
263
+ type_id: number;
264
+ format: string | null;
265
+ type_schema: string | null;
266
+ name: string;
267
+ }[] | null;
268
+ };
269
+ is_set_returning: boolean;
270
+ rows_estimate: number | null;
271
+ setof_options: {
272
+ from: string;
273
+ to: string;
274
+ is_one_to_one: boolean;
275
+ is_setof_return: boolean;
276
+ } | null;
277
+ computed_field_of: number | null;
278
+ error: string | null;
279
+ }, {}>;
280
+ export type RpcFunctionOverload = typeof rpcFunctionOverloadSchema.infer;
281
+ /**
282
+ * A function PostgREST exposes under `/rpc`, with its overloads resolved.
283
+ * Produced by `resolveRpcFunctions` from the raw `functions` collection.
284
+ */
285
+ declare const rpcFunctionSchema: import("arktype/internal/variants/object.ts").ObjectType<{
286
+ schema: string;
287
+ name: string;
288
+ overloads: {
289
+ function_id: number;
290
+ args: {
291
+ type_id: number;
292
+ format: string | null;
293
+ type_schema: string | null;
294
+ name: string;
295
+ mode: "in" | "inout" | "variadic";
296
+ has_default: boolean;
297
+ relation_id: number | null;
298
+ }[];
299
+ returns: {
300
+ type_id: number;
301
+ format: string | null;
302
+ type_schema: string | null;
303
+ relation_id: number | null;
304
+ columns: {
305
+ type_id: number;
306
+ format: string | null;
307
+ type_schema: string | null;
308
+ name: string;
309
+ }[] | null;
310
+ };
311
+ is_set_returning: boolean;
312
+ rows_estimate: number | null;
313
+ setof_options: {
314
+ from: string;
315
+ to: string;
316
+ is_one_to_one: boolean;
317
+ is_setof_return: boolean;
318
+ } | null;
319
+ computed_field_of: number | null;
320
+ error: string | null;
321
+ }[];
322
+ }, {}>;
323
+ export type RpcFunction = typeof rpcFunctionSchema.infer;
211
324
  /**
212
325
  * Bumped whenever `GeneratorMetadata`'s shape changes in a way a consumer
213
326
  * should branch on. Out-of-process consumers (a generator in another
@@ -341,6 +454,44 @@ export declare const generatorMetadataSchema: import("arktype/internal/variants/
341
454
  comment: string | null;
342
455
  type_relation_id: number | null;
343
456
  }[];
457
+ rpcFunctions?: {
458
+ schema: string;
459
+ name: string;
460
+ overloads: {
461
+ function_id: number;
462
+ args: {
463
+ type_id: number;
464
+ format: string | null;
465
+ type_schema: string | null;
466
+ name: string;
467
+ mode: "in" | "inout" | "variadic";
468
+ has_default: boolean;
469
+ relation_id: number | null;
470
+ }[];
471
+ returns: {
472
+ type_id: number;
473
+ format: string | null;
474
+ type_schema: string | null;
475
+ relation_id: number | null;
476
+ columns: {
477
+ type_id: number;
478
+ format: string | null;
479
+ type_schema: string | null;
480
+ name: string;
481
+ }[] | null;
482
+ };
483
+ is_set_returning: boolean;
484
+ rows_estimate: number | null;
485
+ setof_options: {
486
+ from: string;
487
+ to: string;
488
+ is_one_to_one: boolean;
489
+ is_setof_return: boolean;
490
+ } | null;
491
+ computed_field_of: number | null;
492
+ error: string | null;
493
+ }[];
494
+ }[] | undefined;
344
495
  }, {}>;
345
496
  export type GeneratorMetadata = typeof generatorMetadataSchema.infer;
346
497
  /**