@supabase/postgrest-typegen 0.0.1
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/LICENSE +21 -0
- package/README.md +133 -0
- package/dist/generation/constants.d.ts +9 -0
- package/dist/generation/constants.js +10 -0
- package/dist/generation/go.d.ts +6 -0
- package/dist/generation/go.js +233 -0
- package/dist/generation/index.d.ts +5 -0
- package/dist/generation/index.js +5 -0
- package/dist/generation/python.d.ts +6 -0
- package/dist/generation/python.js +339 -0
- package/dist/generation/swift.d.ts +11 -0
- package/dist/generation/swift.js +309 -0
- package/dist/generation/typescript.d.ts +32 -0
- package/dist/generation/typescript.js +796 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/introspection/index.d.ts +27 -0
- package/dist/introspection/index.js +70 -0
- package/dist/introspection/normalize.d.ts +31 -0
- package/dist/introspection/normalize.js +65 -0
- package/dist/introspection/relationships.d.ts +47 -0
- package/dist/introspection/relationships.js +85 -0
- package/dist/introspection/sql/columns.sql.d.ts +7 -0
- package/dist/introspection/sql/columns.sql.js +122 -0
- package/dist/introspection/sql/common.d.ts +16 -0
- package/dist/introspection/sql/common.js +1 -0
- package/dist/introspection/sql/foreign_tables.sql.d.ts +6 -0
- package/dist/introspection/sql/foreign_tables.sql.js +18 -0
- package/dist/introspection/sql/functions.sql.d.ts +5 -0
- package/dist/introspection/sql/functions.sql.js +148 -0
- package/dist/introspection/sql/helpers.d.ts +12 -0
- package/dist/introspection/sql/helpers.js +28 -0
- package/dist/introspection/sql/materialized_views.sql.d.ts +4 -0
- package/dist/introspection/sql/materialized_views.sql.js +19 -0
- package/dist/introspection/sql/primary_keys.sql.d.ts +4 -0
- package/dist/introspection/sql/primary_keys.sql.js +32 -0
- package/dist/introspection/sql/schemas.sql.d.ts +6 -0
- package/dist/introspection/sql/schemas.sql.js +24 -0
- package/dist/introspection/sql/table.sql.d.ts +4 -0
- package/dist/introspection/sql/table.sql.js +41 -0
- package/dist/introspection/sql/table_relationships.sql.d.ts +2 -0
- package/dist/introspection/sql/table_relationships.sql.js +49 -0
- package/dist/introspection/sql/types.sql.d.ts +5 -0
- package/dist/introspection/sql/types.sql.js +65 -0
- package/dist/introspection/sql/views.sql.d.ts +4 -0
- package/dist/introspection/sql/views.sql.js +20 -0
- package/dist/introspection/sql/views_key_dependencies.sql.d.ts +2 -0
- package/dist/introspection/sql/views_key_dependencies.sql.js +195 -0
- package/dist/sort.d.ts +35 -0
- package/dist/sort.js +48 -0
- package/dist/types.d.ts +372 -0
- package/dist/types.js +207 -0
- package/package.json +87 -0
- package/src/generation/constants.ts +11 -0
- package/src/generation/go.ts +356 -0
- package/src/generation/index.ts +13 -0
- package/src/generation/python.ts +461 -0
- package/src/generation/swift.ts +481 -0
- package/src/generation/typescript.ts +1144 -0
- package/src/index.ts +4 -0
- package/src/introspection/index.ts +124 -0
- package/src/introspection/normalize.ts +71 -0
- package/src/introspection/relationships.ts +173 -0
- package/src/introspection/sql/columns.sql.ts +131 -0
- package/src/introspection/sql/common.ts +18 -0
- package/src/introspection/sql/foreign_tables.sql.ts +26 -0
- package/src/introspection/sql/functions.sql.ts +159 -0
- package/src/introspection/sql/helpers.ts +33 -0
- package/src/introspection/sql/materialized_views.sql.ts +25 -0
- package/src/introspection/sql/primary_keys.sql.ts +38 -0
- package/src/introspection/sql/schemas.sql.ts +32 -0
- package/src/introspection/sql/table.sql.ts +47 -0
- package/src/introspection/sql/table_relationships.sql.ts +53 -0
- package/src/introspection/sql/types.sql.ts +74 -0
- package/src/introspection/sql/views.sql.ts +26 -0
- package/src/introspection/sql/views_key_dependencies.sql.ts +199 -0
- package/src/sort.ts +102 -0
- package/src/types.ts +237 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
// oxlint-disable typescript/restrict-template-expressions -- this is a verbatim port of
|
|
2
|
+
// postgres-meta's template, which intentionally interpolates arrays/values into template
|
|
3
|
+
// literals (relying on Array#toString) to build the output. Preserving that is required for
|
|
4
|
+
// byte-parity, and the offending expressions sit inside template literals where a targeted
|
|
5
|
+
// disable comment would corrupt the emitted string.
|
|
6
|
+
import prettier from "prettier";
|
|
7
|
+
import { VALID_FUNCTION_ARGS_MODE, VALID_UNNAMED_FUNCTION_ARG_TYPES, } from "./constants.js";
|
|
8
|
+
/**
|
|
9
|
+
* Does not re-sort collections; pass input pre-sorted with
|
|
10
|
+
* `sortGeneratorMetadata` first. Still merge-sorts tables+foreign tables and
|
|
11
|
+
* views+materialized views per schema, since that cross-collection grouping
|
|
12
|
+
* can't be expressed by the single-collection `sortGeneratorMetadata` pass.
|
|
13
|
+
*/
|
|
14
|
+
export const generateTypescript = async (metadata, opts = {}) => {
|
|
15
|
+
const { schemas, tables, foreignTables, views, materializedViews, columns, relationships, functions, types, } = metadata;
|
|
16
|
+
const { detectOneToOneRelationships = false, postgrestVersion, defaultSchema = "public", } = opts;
|
|
17
|
+
// Ordering of all collections (incl. relationships, columns, args below) is
|
|
18
|
+
// provided by `sortGeneratorMetadata`; this generator no longer re-sorts.
|
|
19
|
+
const introspectionBySchema = Object.fromEntries(schemas.map((s) => [
|
|
20
|
+
s.name,
|
|
21
|
+
{ tables: [], views: [], functions: [], enums: [], compositeTypes: [] },
|
|
22
|
+
]));
|
|
23
|
+
const columnsByTableId = {};
|
|
24
|
+
const tablesNamesByTableId = {};
|
|
25
|
+
const relationTypeByIds = new Map();
|
|
26
|
+
// group types by id for quicker lookup
|
|
27
|
+
const typesById = new Map();
|
|
28
|
+
const tablesLike = [
|
|
29
|
+
...tables,
|
|
30
|
+
...foreignTables,
|
|
31
|
+
...views,
|
|
32
|
+
...materializedViews,
|
|
33
|
+
];
|
|
34
|
+
for (const tableLike of tablesLike) {
|
|
35
|
+
columnsByTableId[tableLike.id] = [];
|
|
36
|
+
tablesNamesByTableId[tableLike.id] = tableLike.name;
|
|
37
|
+
}
|
|
38
|
+
for (const column of columns) {
|
|
39
|
+
if (column.table_id in columnsByTableId) {
|
|
40
|
+
columnsByTableId[column.table_id].push(column);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const type of types) {
|
|
44
|
+
typesById.set(type.id, type);
|
|
45
|
+
// Save all the types that are relation types for quicker lookup
|
|
46
|
+
if (type.type_relation_id) {
|
|
47
|
+
relationTypeByIds.set(type.id, type);
|
|
48
|
+
}
|
|
49
|
+
if (type.schema in introspectionBySchema) {
|
|
50
|
+
if (type.enums.length > 0) {
|
|
51
|
+
introspectionBySchema[type.schema].enums.push(type);
|
|
52
|
+
}
|
|
53
|
+
if (type.attributes.length > 0) {
|
|
54
|
+
introspectionBySchema[type.schema].compositeTypes.push(type);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const relationshipsByRelation = new Map();
|
|
59
|
+
for (const relationship of relationships) {
|
|
60
|
+
const key = `${relationship.schema}.${relationship.relation}`;
|
|
61
|
+
let bucket = relationshipsByRelation.get(key);
|
|
62
|
+
if (!bucket) {
|
|
63
|
+
bucket = [];
|
|
64
|
+
relationshipsByRelation.set(key, bucket);
|
|
65
|
+
}
|
|
66
|
+
bucket.push(relationship);
|
|
67
|
+
}
|
|
68
|
+
function getRelationships(object) {
|
|
69
|
+
const candidates = relationshipsByRelation.get(`${object.schema}.${object.name}`);
|
|
70
|
+
if (!candidates)
|
|
71
|
+
return [];
|
|
72
|
+
return candidates.filter((relationship) => relationship.referenced_schema === object.schema);
|
|
73
|
+
}
|
|
74
|
+
function generateRelationshiptTsDefinition(relationship) {
|
|
75
|
+
return `{
|
|
76
|
+
foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
|
|
77
|
+
columns: ${JSON.stringify(relationship.columns)}${detectOneToOneRelationships
|
|
78
|
+
? `\nisOneToOne: ${relationship.is_one_to_one}`
|
|
79
|
+
: ""}
|
|
80
|
+
referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
|
|
81
|
+
referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
|
|
82
|
+
}`;
|
|
83
|
+
}
|
|
84
|
+
for (const table of tables) {
|
|
85
|
+
if (table.schema in introspectionBySchema) {
|
|
86
|
+
introspectionBySchema[table.schema].tables.push({
|
|
87
|
+
table,
|
|
88
|
+
relationships: getRelationships(table),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const table of foreignTables) {
|
|
93
|
+
if (table.schema in introspectionBySchema) {
|
|
94
|
+
introspectionBySchema[table.schema].tables.push({
|
|
95
|
+
table,
|
|
96
|
+
relationships: getRelationships(table),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const view of views) {
|
|
101
|
+
if (view.schema in introspectionBySchema) {
|
|
102
|
+
introspectionBySchema[view.schema].views.push({
|
|
103
|
+
view,
|
|
104
|
+
relationships: getRelationships(view),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const materializedView of materializedViews) {
|
|
109
|
+
if (materializedView.schema in introspectionBySchema) {
|
|
110
|
+
introspectionBySchema[materializedView.schema].views.push({
|
|
111
|
+
view: {
|
|
112
|
+
...materializedView,
|
|
113
|
+
is_updatable: false,
|
|
114
|
+
},
|
|
115
|
+
relationships: getRelationships(materializedView),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Helper function to get table/view name from relation id
|
|
120
|
+
const getTableNameFromRelationId = (relationId, returnTypeId) => {
|
|
121
|
+
if (!relationId)
|
|
122
|
+
return null;
|
|
123
|
+
if (tablesNamesByTableId[relationId])
|
|
124
|
+
return tablesNamesByTableId[relationId];
|
|
125
|
+
// if it's a composite type we use the type name as relation name to allow sub-selecting fields of the composite type
|
|
126
|
+
const reltype = returnTypeId ? relationTypeByIds.get(returnTypeId) : null;
|
|
127
|
+
return reltype ? reltype.name : null;
|
|
128
|
+
};
|
|
129
|
+
for (const func of functions) {
|
|
130
|
+
if (func.schema in introspectionBySchema) {
|
|
131
|
+
// Get all input args (in, inout, variadic modes)
|
|
132
|
+
const inArgs = func.args.filter(({ mode }) => VALID_FUNCTION_ARGS_MODE.has(mode));
|
|
133
|
+
if (
|
|
134
|
+
// Case 1: Function has no parameters
|
|
135
|
+
inArgs.length === 0 ||
|
|
136
|
+
// Case 2: All input args are named
|
|
137
|
+
!inArgs.some(({ name }) => name === "") ||
|
|
138
|
+
// Case 3: All unnamed args have default values AND are valid types
|
|
139
|
+
inArgs.every((arg) => {
|
|
140
|
+
if (arg.name === "") {
|
|
141
|
+
return (arg.has_default &&
|
|
142
|
+
VALID_UNNAMED_FUNCTION_ARG_TYPES.has(arg.type_id));
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}) ||
|
|
146
|
+
// Case 4: Single unnamed parameter of valid type (json, jsonb, text)
|
|
147
|
+
// Exclude all functions definitions that have only one single argument unnamed argument that isn't
|
|
148
|
+
// a json/jsonb/text as it won't be considered by PostgREST
|
|
149
|
+
(inArgs.length === 1 &&
|
|
150
|
+
inArgs[0].name === "" &&
|
|
151
|
+
(VALID_UNNAMED_FUNCTION_ARG_TYPES.has(inArgs[0].type_id) ||
|
|
152
|
+
// OR if the function have a single unnamed args which is another table (embeded function)
|
|
153
|
+
(relationTypeByIds.get(inArgs[0].type_id) &&
|
|
154
|
+
getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id)) ||
|
|
155
|
+
// OR if the function takes a table row but doesn't qualify as embedded (for error reporting)
|
|
156
|
+
(relationTypeByIds.get(inArgs[0].type_id) &&
|
|
157
|
+
!getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id))))) {
|
|
158
|
+
introspectionBySchema[func.schema].functions.push({ fn: func, inArgs });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// These per-schema groups MERGE multiple metadata collections — `.tables`
|
|
163
|
+
// combines tables + foreign tables, `.views` combines views + materialized
|
|
164
|
+
// views — so the cross-collection name sort can't be expressed by the
|
|
165
|
+
// single-collection `sortGeneratorMetadata` pass and stays here.
|
|
166
|
+
for (const schema in introspectionBySchema) {
|
|
167
|
+
introspectionBySchema[schema].tables.sort((a, b) => a.table.name.localeCompare(b.table.name));
|
|
168
|
+
introspectionBySchema[schema].views.sort((a, b) => a.view.name.localeCompare(b.view.name));
|
|
169
|
+
introspectionBySchema[schema].functions.sort((a, b) => a.fn.name.localeCompare(b.fn.name));
|
|
170
|
+
introspectionBySchema[schema].enums.sort((a, b) => a.name.localeCompare(b.name));
|
|
171
|
+
introspectionBySchema[schema].compositeTypes.sort((a, b) => a.name.localeCompare(b.name));
|
|
172
|
+
}
|
|
173
|
+
const getFunctionTsReturnType = (fn, returnType) => {
|
|
174
|
+
// Determine if this function should have SetofOptions
|
|
175
|
+
let setofOptionsInfo = "";
|
|
176
|
+
const returnTableName = getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id);
|
|
177
|
+
const returnsSetOfTable = fn.is_set_returning_function && fn.return_type_relation_id !== null;
|
|
178
|
+
const returnsMultipleRows = fn.prorows !== null && fn.prorows > 1;
|
|
179
|
+
// Case 1: if the function returns a table, we need to add SetofOptions to allow selecting sub fields of the table
|
|
180
|
+
// Those can be used in rpc to select sub fields of a table
|
|
181
|
+
if (returnTableName) {
|
|
182
|
+
setofOptionsInfo = `SetofOptions: {
|
|
183
|
+
from: "*"
|
|
184
|
+
to: ${JSON.stringify(returnTableName)}
|
|
185
|
+
isOneToOne: ${Boolean(!returnsMultipleRows)}
|
|
186
|
+
isSetofReturn: ${fn.is_set_returning_function}
|
|
187
|
+
}`;
|
|
188
|
+
}
|
|
189
|
+
// Case 2: if the function has a single table argument, we need to add SetofOptions to allow selecting sub fields of the table
|
|
190
|
+
// and set the right "from" and "to" values to allow selecting from a table row
|
|
191
|
+
if (fn.args.length === 1) {
|
|
192
|
+
const relationType = relationTypeByIds.get(fn.args[0].type_id);
|
|
193
|
+
// Only add SetofOptions for functions with table arguments (embedded functions)
|
|
194
|
+
// or specific functions that RETURNS table-name
|
|
195
|
+
if (relationType) {
|
|
196
|
+
const sourceTable = relationType.format;
|
|
197
|
+
// Case 1: Standard embedded function with proper setof detection
|
|
198
|
+
if (returnsSetOfTable && returnTableName) {
|
|
199
|
+
setofOptionsInfo = `SetofOptions: {
|
|
200
|
+
from: ${JSON.stringify(sourceTable)}
|
|
201
|
+
to: ${JSON.stringify(returnTableName)}
|
|
202
|
+
isOneToOne: ${Boolean(!returnsMultipleRows)}
|
|
203
|
+
isSetofReturn: true
|
|
204
|
+
}`;
|
|
205
|
+
} // Case 2: Handle RETURNS table-name those are always a one to one relationship
|
|
206
|
+
else if (returnTableName && !returnsSetOfTable) {
|
|
207
|
+
const targetTable = returnTableName;
|
|
208
|
+
setofOptionsInfo = `SetofOptions: {
|
|
209
|
+
from: ${JSON.stringify(sourceTable)}
|
|
210
|
+
to: ${JSON.stringify(targetTable)}
|
|
211
|
+
isOneToOne: true
|
|
212
|
+
isSetofReturn: false
|
|
213
|
+
}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// A ROWS 1 estimate may only collapse the array type when SetofOptions is also emitted
|
|
218
|
+
// (returnTableName resolved): postgrest-js relies on isOneToOne to restore the array shape
|
|
219
|
+
// for direct RPC calls. Without that metadata PostgREST still returns an array, so the
|
|
220
|
+
// collapse would make the type diverge from the actual response.
|
|
221
|
+
const collapsibleSingleRow = !returnsMultipleRows && returnTableName !== null;
|
|
222
|
+
return `${returnType}${fn.is_set_returning_function && !collapsibleSingleRow ? "[]" : ""}
|
|
223
|
+
${setofOptionsInfo ? `${setofOptionsInfo}` : ""}`;
|
|
224
|
+
};
|
|
225
|
+
const getFunctionReturnType = (schema, fn) => {
|
|
226
|
+
// Case 1: `returns table`.
|
|
227
|
+
const tableArgs = fn.args.filter(({ mode }) => mode === "table");
|
|
228
|
+
if (tableArgs.length > 0) {
|
|
229
|
+
const argsNameAndType = tableArgs.map(({ name, type_id }) => {
|
|
230
|
+
const type = typesById.get(type_id);
|
|
231
|
+
let tsType = "unknown";
|
|
232
|
+
if (type) {
|
|
233
|
+
tsType = pgTypeToTsType(schema, type.name, {
|
|
234
|
+
types,
|
|
235
|
+
schemas,
|
|
236
|
+
tables,
|
|
237
|
+
views,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return { name, type: tsType };
|
|
241
|
+
});
|
|
242
|
+
return `{
|
|
243
|
+
${argsNameAndType.map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)}
|
|
244
|
+
}`;
|
|
245
|
+
}
|
|
246
|
+
// Case 2: returns a relation's row type.
|
|
247
|
+
const relation = introspectionBySchema[schema.name]?.tables.find(({ table: { id } }) => id === fn.return_type_relation_id)?.table ||
|
|
248
|
+
introspectionBySchema[schema.name]?.views.find(({ view: { id } }) => id === fn.return_type_relation_id)?.view;
|
|
249
|
+
if (relation) {
|
|
250
|
+
return `{
|
|
251
|
+
${columnsByTableId[relation.id]
|
|
252
|
+
.map((column) => generateColumnTsDefinition(schema, {
|
|
253
|
+
name: column.name,
|
|
254
|
+
format: column.format,
|
|
255
|
+
type_schema: column.type_schema,
|
|
256
|
+
is_nullable: column.is_nullable,
|
|
257
|
+
is_optional: false,
|
|
258
|
+
}, {
|
|
259
|
+
types,
|
|
260
|
+
schemas,
|
|
261
|
+
tables,
|
|
262
|
+
views,
|
|
263
|
+
}))
|
|
264
|
+
.join(",\n")}
|
|
265
|
+
}`;
|
|
266
|
+
}
|
|
267
|
+
// Case 3: returns base/array/composite/enum type.
|
|
268
|
+
const type = typesById.get(fn.return_type_id);
|
|
269
|
+
if (type) {
|
|
270
|
+
return pgTypeToTsType(schema, type.name, {
|
|
271
|
+
types,
|
|
272
|
+
schemas,
|
|
273
|
+
tables,
|
|
274
|
+
views,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return "unknown";
|
|
278
|
+
};
|
|
279
|
+
// Special error case for functions that take table row but don't qualify as embedded functions
|
|
280
|
+
const hasTableRowError = (fn, inArgs) => {
|
|
281
|
+
if (inArgs.length === 1 &&
|
|
282
|
+
inArgs[0].name === "" &&
|
|
283
|
+
relationTypeByIds.get(inArgs[0].type_id) &&
|
|
284
|
+
!getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id)) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
};
|
|
289
|
+
// Check for generic conflict cases that need error reporting
|
|
290
|
+
const getConflictError = (schema, fns, fn, inArgs) => {
|
|
291
|
+
// If there is a single function definition, there is no conflict
|
|
292
|
+
if (fns.length <= 1)
|
|
293
|
+
return null;
|
|
294
|
+
// Generic conflict detection patterns
|
|
295
|
+
// Pattern 1: No-args vs default-args conflicts
|
|
296
|
+
if (inArgs.length === 0) {
|
|
297
|
+
const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
|
|
298
|
+
if (otherFn === fn)
|
|
299
|
+
return false;
|
|
300
|
+
return (otherInArgs.length === 1 &&
|
|
301
|
+
otherInArgs[0].name === "" &&
|
|
302
|
+
otherInArgs[0].has_default);
|
|
303
|
+
});
|
|
304
|
+
if (conflictingFns.length > 0) {
|
|
305
|
+
const conflictingFn = conflictingFns[0];
|
|
306
|
+
const returnTypeName = typesById.get(conflictingFn.fn.return_type_id)?.name || "unknown";
|
|
307
|
+
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`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Pattern 2: Same parameter name but different types (unresolvable overloads)
|
|
311
|
+
if (inArgs.length === 1 && inArgs[0].name !== "") {
|
|
312
|
+
const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
|
|
313
|
+
if (otherFn === fn)
|
|
314
|
+
return false;
|
|
315
|
+
return (otherInArgs.length === 1 &&
|
|
316
|
+
otherInArgs[0].name === inArgs[0].name &&
|
|
317
|
+
otherInArgs[0].type_id !== inArgs[0].type_id);
|
|
318
|
+
});
|
|
319
|
+
if (conflictingFns.length > 0) {
|
|
320
|
+
const allConflictingFunctions = [{ fn, inArgs }, ...conflictingFns];
|
|
321
|
+
const conflictList = allConflictingFunctions
|
|
322
|
+
.sort((a, b) => {
|
|
323
|
+
const aArgs = a.inArgs;
|
|
324
|
+
const bArgs = b.inArgs;
|
|
325
|
+
return (aArgs[0]?.type_id || 0) - (bArgs[0]?.type_id || 0);
|
|
326
|
+
})
|
|
327
|
+
.map((f) => {
|
|
328
|
+
const args = f.inArgs;
|
|
329
|
+
return `${schema.name}.${fn.name}(${args
|
|
330
|
+
.map((a) => `${a.name || ""} => ${typesById.get(a.type_id)?.name || "unknown"}`)
|
|
331
|
+
.join(", ")})`;
|
|
332
|
+
})
|
|
333
|
+
.join(", ");
|
|
334
|
+
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`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
};
|
|
339
|
+
const getFunctionSignatures = (schema, fns) => {
|
|
340
|
+
return fns
|
|
341
|
+
.map(({ fn, inArgs }) => {
|
|
342
|
+
let argsType = "never";
|
|
343
|
+
let returnType = getFunctionReturnType(schema, fn);
|
|
344
|
+
// Check for specific error cases
|
|
345
|
+
const conflictError = getConflictError(schema, fns, fn, inArgs);
|
|
346
|
+
if (conflictError) {
|
|
347
|
+
if (inArgs.length > 0) {
|
|
348
|
+
const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
|
|
349
|
+
const type = typesById.get(type_id);
|
|
350
|
+
let tsType = "unknown";
|
|
351
|
+
if (type) {
|
|
352
|
+
tsType = pgTypeToTsType(schema, type.name, {
|
|
353
|
+
types,
|
|
354
|
+
schemas,
|
|
355
|
+
tables,
|
|
356
|
+
views,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return { name, type: tsType, has_default };
|
|
360
|
+
});
|
|
361
|
+
argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
362
|
+
}
|
|
363
|
+
returnType = `{ error: true } & ${JSON.stringify(conflictError)}`;
|
|
364
|
+
}
|
|
365
|
+
else if (hasTableRowError(fn, inArgs)) {
|
|
366
|
+
// Special case for computed fields returning scalars functions
|
|
367
|
+
if (inArgs.length > 0) {
|
|
368
|
+
const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
|
|
369
|
+
const type = typesById.get(type_id);
|
|
370
|
+
let tsType = "unknown";
|
|
371
|
+
if (type) {
|
|
372
|
+
tsType = pgTypeToTsType(schema, type.name, {
|
|
373
|
+
types,
|
|
374
|
+
schemas,
|
|
375
|
+
tables,
|
|
376
|
+
views,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return { name, type: tsType, has_default };
|
|
380
|
+
});
|
|
381
|
+
argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
382
|
+
}
|
|
383
|
+
returnType = `{ error: true } & ${JSON.stringify(`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`)}`;
|
|
384
|
+
}
|
|
385
|
+
else if (inArgs.length > 0) {
|
|
386
|
+
const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
|
|
387
|
+
const type = typesById.get(type_id);
|
|
388
|
+
let tsType = "unknown";
|
|
389
|
+
if (type) {
|
|
390
|
+
tsType = pgTypeToTsType(schema, type.name, {
|
|
391
|
+
types,
|
|
392
|
+
schemas,
|
|
393
|
+
tables,
|
|
394
|
+
views,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return { name, type: tsType, has_default };
|
|
398
|
+
});
|
|
399
|
+
argsType = `{ ${argsNameAndType.map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
400
|
+
}
|
|
401
|
+
return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(fn, returnType)} }`;
|
|
402
|
+
})
|
|
403
|
+
.join(" |\n");
|
|
404
|
+
};
|
|
405
|
+
const internal_supabase_schema = postgrestVersion
|
|
406
|
+
? `// Allows to automatically instantiate createClient with right options
|
|
407
|
+
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
|
408
|
+
__InternalSupabase: {
|
|
409
|
+
PostgrestVersion: '${postgrestVersion}'
|
|
410
|
+
}`
|
|
411
|
+
: "";
|
|
412
|
+
function generateNullableUnionTsType(tsType, isNullable) {
|
|
413
|
+
// Only add the null union if the type is not unknown as unknown already includes null
|
|
414
|
+
if (tsType === "unknown" || tsType === "any" || !isNullable) {
|
|
415
|
+
return tsType;
|
|
416
|
+
}
|
|
417
|
+
return `${tsType} | null`;
|
|
418
|
+
}
|
|
419
|
+
function generateColumnTsDefinition(schema, column, context) {
|
|
420
|
+
return `${JSON.stringify(column.name)}${column.is_optional ? "?" : ""}: ${generateNullableUnionTsType(pgTypeToTsType(schema, column.format, context, column.type_schema), column.is_nullable)}`;
|
|
421
|
+
}
|
|
422
|
+
let output = `
|
|
423
|
+
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
|
|
424
|
+
|
|
425
|
+
export type Database = {
|
|
426
|
+
${internal_supabase_schema}
|
|
427
|
+
${schemas.map((schema) => {
|
|
428
|
+
const { tables: schemaTables, views: schemaViews, functions: schemaFunctions, enums: schemaEnums, compositeTypes: schemaCompositeTypes, } = introspectionBySchema[schema.name];
|
|
429
|
+
return `${JSON.stringify(schema.name)}: {
|
|
430
|
+
Tables: {
|
|
431
|
+
${schemaTables.length === 0
|
|
432
|
+
? "[_ in never]: never"
|
|
433
|
+
: schemaTables.map(({ table, relationships, }) => `${JSON.stringify(table.name)}: {
|
|
434
|
+
Row: {
|
|
435
|
+
${[
|
|
436
|
+
...columnsByTableId[table.id].map((column) => generateColumnTsDefinition(schema, {
|
|
437
|
+
name: column.name,
|
|
438
|
+
format: column.format,
|
|
439
|
+
type_schema: column.type_schema,
|
|
440
|
+
is_nullable: column.is_nullable,
|
|
441
|
+
is_optional: false,
|
|
442
|
+
}, { types, schemas, tables, views })),
|
|
443
|
+
...schemaFunctions
|
|
444
|
+
.filter(({ fn }) => fn.argument_types === table.name)
|
|
445
|
+
.map(({ fn }) => {
|
|
446
|
+
return `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}`;
|
|
447
|
+
}),
|
|
448
|
+
]}
|
|
449
|
+
}
|
|
450
|
+
Insert: {
|
|
451
|
+
${columnsByTableId[table.id].map((column) => {
|
|
452
|
+
if (column.identity_generation === "ALWAYS") {
|
|
453
|
+
return `${JSON.stringify(column.name)}?: never`;
|
|
454
|
+
}
|
|
455
|
+
return generateColumnTsDefinition(schema, {
|
|
456
|
+
name: column.name,
|
|
457
|
+
format: column.format,
|
|
458
|
+
type_schema: column.type_schema,
|
|
459
|
+
is_nullable: column.is_nullable,
|
|
460
|
+
is_optional: column.is_nullable ||
|
|
461
|
+
column.is_identity ||
|
|
462
|
+
column.default_value !== null,
|
|
463
|
+
}, { types, schemas, tables, views });
|
|
464
|
+
})}
|
|
465
|
+
}
|
|
466
|
+
Update: {
|
|
467
|
+
${columnsByTableId[table.id].map((column) => {
|
|
468
|
+
if (column.identity_generation === "ALWAYS") {
|
|
469
|
+
return `${JSON.stringify(column.name)}?: never`;
|
|
470
|
+
}
|
|
471
|
+
return generateColumnTsDefinition(schema, {
|
|
472
|
+
name: column.name,
|
|
473
|
+
format: column.format,
|
|
474
|
+
type_schema: column.type_schema,
|
|
475
|
+
is_nullable: column.is_nullable,
|
|
476
|
+
is_optional: true,
|
|
477
|
+
}, { types, schemas, tables, views });
|
|
478
|
+
})}
|
|
479
|
+
}
|
|
480
|
+
Relationships: [
|
|
481
|
+
${relationships.map(generateRelationshiptTsDefinition)}
|
|
482
|
+
]
|
|
483
|
+
}`)}
|
|
484
|
+
}
|
|
485
|
+
Views: {
|
|
486
|
+
${schemaViews.length === 0
|
|
487
|
+
? "[_ in never]: never"
|
|
488
|
+
: schemaViews.map(({ view, relationships, }) => `${JSON.stringify(view.name)}: {
|
|
489
|
+
Row: {
|
|
490
|
+
${[
|
|
491
|
+
...columnsByTableId[view.id].map((column) => generateColumnTsDefinition(schema, {
|
|
492
|
+
name: column.name,
|
|
493
|
+
format: column.format,
|
|
494
|
+
type_schema: column.type_schema,
|
|
495
|
+
is_nullable: column.is_nullable,
|
|
496
|
+
is_optional: false,
|
|
497
|
+
}, { types, schemas, tables, views })),
|
|
498
|
+
...schemaFunctions
|
|
499
|
+
.filter(({ fn }) => fn.argument_types === view.name)
|
|
500
|
+
.map(({ fn }) => `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}`),
|
|
501
|
+
]}
|
|
502
|
+
}
|
|
503
|
+
${view.is_updatable
|
|
504
|
+
? `Insert: {
|
|
505
|
+
${columnsByTableId[view.id].map((column) => {
|
|
506
|
+
if (!column.is_updatable) {
|
|
507
|
+
return `${JSON.stringify(column.name)}?: never`;
|
|
508
|
+
}
|
|
509
|
+
return generateColumnTsDefinition(schema, {
|
|
510
|
+
name: column.name,
|
|
511
|
+
format: column.format,
|
|
512
|
+
type_schema: column.type_schema,
|
|
513
|
+
is_nullable: true,
|
|
514
|
+
is_optional: true,
|
|
515
|
+
}, { types, schemas, tables, views });
|
|
516
|
+
})}
|
|
517
|
+
}
|
|
518
|
+
Update: {
|
|
519
|
+
${columnsByTableId[view.id].map((column) => {
|
|
520
|
+
if (!column.is_updatable) {
|
|
521
|
+
return `${JSON.stringify(column.name)}?: never`;
|
|
522
|
+
}
|
|
523
|
+
return generateColumnTsDefinition(schema, {
|
|
524
|
+
name: column.name,
|
|
525
|
+
format: column.format,
|
|
526
|
+
type_schema: column.type_schema,
|
|
527
|
+
is_nullable: true,
|
|
528
|
+
is_optional: true,
|
|
529
|
+
}, { types, schemas, tables, views });
|
|
530
|
+
})}
|
|
531
|
+
}
|
|
532
|
+
`
|
|
533
|
+
: ""}Relationships: [
|
|
534
|
+
${relationships.map(generateRelationshiptTsDefinition)}
|
|
535
|
+
]
|
|
536
|
+
}`)}
|
|
537
|
+
}
|
|
538
|
+
Functions: {
|
|
539
|
+
${(() => {
|
|
540
|
+
if (schemaFunctions.length === 0) {
|
|
541
|
+
return "[_ in never]: never";
|
|
542
|
+
}
|
|
543
|
+
const schemaFunctionsGroupedByName = schemaFunctions.reduce((acc, curr) => {
|
|
544
|
+
acc[curr.fn.name] ??= [];
|
|
545
|
+
acc[curr.fn.name].push(curr);
|
|
546
|
+
return acc;
|
|
547
|
+
}, {});
|
|
548
|
+
for (const fnName in schemaFunctionsGroupedByName) {
|
|
549
|
+
schemaFunctionsGroupedByName[fnName].sort((a, b) => a.fn.argument_types.localeCompare(b.fn.argument_types) ||
|
|
550
|
+
a.fn.return_type.localeCompare(b.fn.return_type));
|
|
551
|
+
}
|
|
552
|
+
return Object.entries(schemaFunctionsGroupedByName)
|
|
553
|
+
.map(([fnName, fns]) => {
|
|
554
|
+
const functionSignatures = getFunctionSignatures(schema, fns);
|
|
555
|
+
return `${JSON.stringify(fnName)}:\n${functionSignatures}`;
|
|
556
|
+
})
|
|
557
|
+
.join(",\n");
|
|
558
|
+
})()}
|
|
559
|
+
}
|
|
560
|
+
Enums: {
|
|
561
|
+
${schemaEnums.length === 0
|
|
562
|
+
? "[_ in never]: never"
|
|
563
|
+
: schemaEnums.map((enum_) => `${JSON.stringify(enum_.name)}: ${enum_.enums
|
|
564
|
+
.map((variant) => JSON.stringify(variant))
|
|
565
|
+
.join("|")}`)}
|
|
566
|
+
}
|
|
567
|
+
CompositeTypes: {
|
|
568
|
+
${schemaCompositeTypes.length === 0
|
|
569
|
+
? "[_ in never]: never"
|
|
570
|
+
: schemaCompositeTypes.map(({ name, attributes }) => `${JSON.stringify(name)}: {
|
|
571
|
+
${attributes.map(({ name, type_id }) => {
|
|
572
|
+
const type = typesById.get(type_id);
|
|
573
|
+
let tsType = "unknown";
|
|
574
|
+
if (type) {
|
|
575
|
+
tsType = `${generateNullableUnionTsType(pgTypeToTsType(schema, type.name, {
|
|
576
|
+
types,
|
|
577
|
+
schemas,
|
|
578
|
+
tables,
|
|
579
|
+
views,
|
|
580
|
+
}), true)}`;
|
|
581
|
+
}
|
|
582
|
+
return `${JSON.stringify(name)}: ${tsType}`;
|
|
583
|
+
})}
|
|
584
|
+
}`)}
|
|
585
|
+
}
|
|
586
|
+
}`;
|
|
587
|
+
})}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
|
|
591
|
+
|
|
592
|
+
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(defaultSchema)}>]
|
|
593
|
+
|
|
594
|
+
export type Tables<
|
|
595
|
+
DefaultSchemaTableNameOrOptions extends
|
|
596
|
+
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
|
597
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
598
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
599
|
+
schema: keyof DatabaseWithoutInternals
|
|
600
|
+
}
|
|
601
|
+
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
|
602
|
+
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
|
|
603
|
+
: never = never
|
|
604
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
605
|
+
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
|
606
|
+
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
|
607
|
+
Row: infer R
|
|
608
|
+
}
|
|
609
|
+
? R
|
|
610
|
+
: never
|
|
611
|
+
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
|
612
|
+
? (DefaultSchema["Tables"] & DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
|
613
|
+
Row: infer R
|
|
614
|
+
}
|
|
615
|
+
? R
|
|
616
|
+
: never
|
|
617
|
+
: never
|
|
618
|
+
|
|
619
|
+
export type TablesInsert<
|
|
620
|
+
DefaultSchemaTableNameOrOptions extends
|
|
621
|
+
| keyof DefaultSchema["Tables"]
|
|
622
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
623
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
624
|
+
schema: keyof DatabaseWithoutInternals
|
|
625
|
+
}
|
|
626
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
|
627
|
+
: never = never
|
|
628
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
629
|
+
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
|
630
|
+
Insert: infer I
|
|
631
|
+
}
|
|
632
|
+
? I
|
|
633
|
+
: never
|
|
634
|
+
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
|
635
|
+
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
|
636
|
+
Insert: infer I
|
|
637
|
+
}
|
|
638
|
+
? I
|
|
639
|
+
: never
|
|
640
|
+
: never
|
|
641
|
+
|
|
642
|
+
export type TablesUpdate<
|
|
643
|
+
DefaultSchemaTableNameOrOptions extends
|
|
644
|
+
| keyof DefaultSchema["Tables"]
|
|
645
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
646
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
647
|
+
schema: keyof DatabaseWithoutInternals
|
|
648
|
+
}
|
|
649
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
|
650
|
+
: never = never
|
|
651
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
652
|
+
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
|
653
|
+
Update: infer U
|
|
654
|
+
}
|
|
655
|
+
? U
|
|
656
|
+
: never
|
|
657
|
+
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
|
658
|
+
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
|
659
|
+
Update: infer U
|
|
660
|
+
}
|
|
661
|
+
? U
|
|
662
|
+
: never
|
|
663
|
+
: never
|
|
664
|
+
|
|
665
|
+
export type Enums<
|
|
666
|
+
DefaultSchemaEnumNameOrOptions extends
|
|
667
|
+
| keyof DefaultSchema["Enums"]
|
|
668
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
669
|
+
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
|
670
|
+
schema: keyof DatabaseWithoutInternals
|
|
671
|
+
}
|
|
672
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
|
|
673
|
+
: never = never
|
|
674
|
+
> = DefaultSchemaEnumNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
675
|
+
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
|
676
|
+
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
|
|
677
|
+
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
|
678
|
+
: never
|
|
679
|
+
|
|
680
|
+
export type CompositeTypes<
|
|
681
|
+
PublicCompositeTypeNameOrOptions extends
|
|
682
|
+
| keyof DefaultSchema["CompositeTypes"]
|
|
683
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
684
|
+
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
|
685
|
+
schema: keyof DatabaseWithoutInternals
|
|
686
|
+
}
|
|
687
|
+
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
|
|
688
|
+
: never = never
|
|
689
|
+
> = PublicCompositeTypeNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
690
|
+
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
|
|
691
|
+
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
|
|
692
|
+
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
|
693
|
+
: never
|
|
694
|
+
|
|
695
|
+
export const Constants = {
|
|
696
|
+
${schemas.map((schema) => {
|
|
697
|
+
const schemaEnums = introspectionBySchema[schema.name].enums;
|
|
698
|
+
return `${JSON.stringify(schema.name)}: {
|
|
699
|
+
Enums: {
|
|
700
|
+
${schemaEnums.map((enum_) => `${JSON.stringify(enum_.name)}: [${enum_.enums
|
|
701
|
+
.map((variant) => JSON.stringify(variant))
|
|
702
|
+
.join(", ")}]`)}
|
|
703
|
+
}
|
|
704
|
+
}`;
|
|
705
|
+
})}
|
|
706
|
+
} as const
|
|
707
|
+
`;
|
|
708
|
+
output = await prettier.format(output, {
|
|
709
|
+
parser: "typescript",
|
|
710
|
+
semi: false,
|
|
711
|
+
});
|
|
712
|
+
return output;
|
|
713
|
+
};
|
|
714
|
+
// TODO: Make this more robust. Currently doesn't handle range types - returns them as unknown.
|
|
715
|
+
export const pgTypeToTsType = (schema, pgType, { types, schemas, tables, views, },
|
|
716
|
+
// The schema that actually owns `pgType` (a column's `type_schema`), when
|
|
717
|
+
// known. Disambiguates same-named enums/composites across schemas; falls
|
|
718
|
+
// back to `schema.name` (the table's own schema) when not provided, e.g.
|
|
719
|
+
// for function args/return types where no equivalent field exists yet.
|
|
720
|
+
typeSchema) => {
|
|
721
|
+
if (pgType === "bool") {
|
|
722
|
+
return "boolean";
|
|
723
|
+
}
|
|
724
|
+
else if (["int2", "int4", "int8", "float4", "float8", "numeric"].includes(pgType)) {
|
|
725
|
+
return "number";
|
|
726
|
+
}
|
|
727
|
+
else if ([
|
|
728
|
+
"bytea",
|
|
729
|
+
"bpchar",
|
|
730
|
+
"varchar",
|
|
731
|
+
"date",
|
|
732
|
+
"text",
|
|
733
|
+
"citext",
|
|
734
|
+
"time",
|
|
735
|
+
"timetz",
|
|
736
|
+
"timestamp",
|
|
737
|
+
"timestamptz",
|
|
738
|
+
"uuid",
|
|
739
|
+
"vector",
|
|
740
|
+
"interval",
|
|
741
|
+
].includes(pgType)) {
|
|
742
|
+
return "string";
|
|
743
|
+
}
|
|
744
|
+
else if (["json", "jsonb"].includes(pgType)) {
|
|
745
|
+
return "Json";
|
|
746
|
+
}
|
|
747
|
+
else if (pgType === "void") {
|
|
748
|
+
return "undefined";
|
|
749
|
+
}
|
|
750
|
+
else if (pgType === "record") {
|
|
751
|
+
return "Record<string, unknown>";
|
|
752
|
+
}
|
|
753
|
+
else if (pgType.startsWith("_")) {
|
|
754
|
+
return `(${pgTypeToTsType(schema, pgType.substring(1), { types, schemas, tables, views }, typeSchema)})[]`;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
const preferredSchema = typeSchema ?? schema.name;
|
|
758
|
+
const enumTypes = types.filter((type) => type.name === pgType && type.enums.length > 0);
|
|
759
|
+
if (enumTypes.length > 0) {
|
|
760
|
+
const enumType = enumTypes.find((type) => type.schema === preferredSchema) ||
|
|
761
|
+
enumTypes[0];
|
|
762
|
+
if (schemas.some(({ name }) => name === enumType.schema)) {
|
|
763
|
+
return `Database[${JSON.stringify(enumType.schema)}]['Enums'][${JSON.stringify(enumType.name)}]`;
|
|
764
|
+
}
|
|
765
|
+
return enumType.enums.map((variant) => JSON.stringify(variant)).join("|");
|
|
766
|
+
}
|
|
767
|
+
const compositeTypes = types.filter((type) => type.name === pgType && type.attributes.length > 0);
|
|
768
|
+
if (compositeTypes.length > 0) {
|
|
769
|
+
const compositeType = compositeTypes.find((type) => type.schema === preferredSchema) ||
|
|
770
|
+
compositeTypes[0];
|
|
771
|
+
if (schemas.some(({ name }) => name === compositeType.schema)) {
|
|
772
|
+
return `Database[${JSON.stringify(compositeType.schema)}]['CompositeTypes'][${JSON.stringify(compositeType.name)}]`;
|
|
773
|
+
}
|
|
774
|
+
return "unknown";
|
|
775
|
+
}
|
|
776
|
+
const tableRowTypes = tables.filter((table) => table.name === pgType);
|
|
777
|
+
if (tableRowTypes.length > 0) {
|
|
778
|
+
const tableRowType = tableRowTypes.find((type) => type.schema === preferredSchema) ||
|
|
779
|
+
tableRowTypes[0];
|
|
780
|
+
if (schemas.some(({ name }) => name === tableRowType.schema)) {
|
|
781
|
+
return `Database[${JSON.stringify(tableRowType.schema)}]['Tables'][${JSON.stringify(tableRowType.name)}]['Row']`;
|
|
782
|
+
}
|
|
783
|
+
return "unknown";
|
|
784
|
+
}
|
|
785
|
+
const viewRowTypes = views.filter((view) => view.name === pgType);
|
|
786
|
+
if (viewRowTypes.length > 0) {
|
|
787
|
+
const viewRowType = viewRowTypes.find((type) => type.schema === preferredSchema) ||
|
|
788
|
+
viewRowTypes[0];
|
|
789
|
+
if (schemas.some(({ name }) => name === viewRowType.schema)) {
|
|
790
|
+
return `Database[${JSON.stringify(viewRowType.schema)}]['Views'][${JSON.stringify(viewRowType.name)}]['Row']`;
|
|
791
|
+
}
|
|
792
|
+
return "unknown";
|
|
793
|
+
}
|
|
794
|
+
return "unknown";
|
|
795
|
+
}
|
|
796
|
+
};
|