@supabase/postgrest-typegen 0.2.1 → 0.2.3
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 +13 -5
- package/dist/collation.d.ts +2 -0
- package/dist/collation.js +15 -0
- package/dist/generation/index.d.ts +1 -1
- package/dist/generation/python.js +21 -15
- package/dist/generation/swift.js +9 -2
- package/dist/generation/typescript.d.ts +18 -7
- package/dist/generation/typescript.js +102 -79
- package/dist/introspection/index.js +6 -23
- package/dist/introspection/relationships.d.ts +4 -4
- package/dist/introspection/relationships.js +4 -11
- package/dist/introspection/sql/columns.sql.d.ts +2 -7
- package/dist/introspection/sql/columns.sql.js +23 -17
- package/dist/introspection/sql/common.d.ts +7 -15
- package/dist/introspection/sql/foreign_tables.sql.d.ts +2 -6
- package/dist/introspection/sql/foreign_tables.sql.js +0 -5
- package/dist/introspection/sql/functions.sql.d.ts +2 -5
- package/dist/introspection/sql/functions.sql.js +0 -33
- package/dist/introspection/sql/materialized_views.sql.d.ts +2 -4
- package/dist/introspection/sql/materialized_views.sql.js +0 -5
- package/dist/introspection/sql/primary_keys.sql.d.ts +2 -4
- package/dist/introspection/sql/primary_keys.sql.js +0 -4
- package/dist/introspection/sql/schemas.sql.d.ts +2 -6
- package/dist/introspection/sql/schemas.sql.js +2 -6
- package/dist/introspection/sql/table.sql.d.ts +2 -4
- package/dist/introspection/sql/table.sql.js +0 -5
- package/dist/introspection/sql/table_relationships.sql.d.ts +2 -2
- package/dist/introspection/sql/types.sql.d.ts +5 -5
- package/dist/introspection/sql/types.sql.js +6 -17
- package/dist/introspection/sql/views.sql.d.ts +2 -4
- package/dist/introspection/sql/views.sql.js +0 -5
- package/dist/introspection/sql/views_key_dependencies.sql.d.ts +2 -2
- package/dist/introspection/sql/views_key_dependencies.sql.js +64 -75
- package/dist/sort.js +32 -16
- package/dist/types.d.ts +4 -4
- package/dist/types.js +6 -4
- package/package.json +4 -4
- package/src/collation.ts +16 -0
- package/src/generation/index.ts +1 -0
- package/src/generation/python.ts +28 -15
- package/src/generation/swift.ts +9 -2
- package/src/generation/typescript.ts +161 -104
- package/src/introspection/index.ts +7 -32
- package/src/introspection/relationships.ts +10 -19
- package/src/introspection/sql/columns.sql.ts +25 -26
- package/src/introspection/sql/common.ts +7 -17
- package/src/introspection/sql/foreign_tables.sql.ts +2 -13
- package/src/introspection/sql/functions.sql.ts +2 -44
- package/src/introspection/sql/materialized_views.sql.ts +2 -11
- package/src/introspection/sql/primary_keys.sql.ts +2 -10
- package/src/introspection/sql/schemas.sql.ts +4 -14
- package/src/introspection/sql/table.sql.ts +2 -11
- package/src/introspection/sql/table_relationships.sql.ts +2 -4
- package/src/introspection/sql/types.sql.ts +6 -26
- package/src/introspection/sql/views.sql.ts +2 -11
- package/src/introspection/sql/views_key_dependencies.sql.ts +83 -77
- package/src/sort.ts +37 -18
- package/src/types.ts +6 -4
package/dist/sort.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
+
import { compareStrings } from "./collation.js";
|
|
1
2
|
// Relations (tables/views/materialized views/foreign tables) and types share a
|
|
2
3
|
// (schema, name) identity within a database; `id` breaks any residual tie.
|
|
3
|
-
const bySchemaName = (a, b) => a.schema
|
|
4
|
-
a.name
|
|
4
|
+
const bySchemaName = (a, b) => compareStrings(a.schema, b.schema) ||
|
|
5
|
+
compareStrings(a.name, b.name) ||
|
|
5
6
|
a.id - b.id;
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
// Column lists compare as their JSON text, matching the TypeScript generator's
|
|
8
|
+
// historical `relationships.sort`.
|
|
9
|
+
const compareColumns = (a, b) => compareStrings(JSON.stringify(a), JSON.stringify(b));
|
|
10
|
+
// The first three keys mirror the TypeScript generator's historical
|
|
11
|
+
// `relationships.sort`. They are not total: `expandViewRelationships` in
|
|
12
|
+
// `introspection/relationships.ts` copies one foreign key onto every view
|
|
13
|
+
// exposing it, on either side, and onto every combination of view columns
|
|
14
|
+
// carrying it. View-to-table copies share the referenced side and differ in
|
|
15
|
+
// `schema`, `relation` and `columns`; table-to-view copies onto same-named
|
|
16
|
+
// views in different schemas differ only in `referenced_schema`. The remaining
|
|
17
|
+
// keys order those copies, since the view key dependency query does not order
|
|
18
|
+
// the view columns it aggregates.
|
|
19
|
+
const byRelationship = (a, b) => compareStrings(a.foreign_key_name, b.foreign_key_name) ||
|
|
20
|
+
compareStrings(a.referenced_relation, b.referenced_relation) ||
|
|
21
|
+
compareColumns(a.referenced_columns, b.referenced_columns) ||
|
|
22
|
+
compareStrings(a.referenced_schema, b.referenced_schema) ||
|
|
23
|
+
compareStrings(a.schema, b.schema) ||
|
|
24
|
+
compareStrings(a.relation, b.relation) ||
|
|
25
|
+
compareColumns(a.columns, b.columns);
|
|
10
26
|
/**
|
|
11
27
|
* Return a new {@link GeneratorMetadata} with every collection ordered by a
|
|
12
28
|
* stable, total, semantic key (matching the TypeScript generator's ordering).
|
|
@@ -16,32 +32,32 @@ const byRelationship = (a, b) => a.foreign_key_name.localeCompare(b.foreign_key_
|
|
|
16
32
|
export function sortGeneratorMetadata(metadata) {
|
|
17
33
|
return {
|
|
18
34
|
version: metadata.version,
|
|
19
|
-
schemas: [...metadata.schemas].sort((a, b) => a.name
|
|
35
|
+
schemas: [...metadata.schemas].sort((a, b) => compareStrings(a.name, b.name) || a.id - b.id),
|
|
20
36
|
tables: [...metadata.tables].sort(bySchemaName),
|
|
21
37
|
foreignTables: [...metadata.foreignTables].sort(bySchemaName),
|
|
22
38
|
views: [...metadata.views].sort(bySchemaName),
|
|
23
39
|
materializedViews: [...metadata.materializedViews].sort(bySchemaName),
|
|
24
40
|
// Group columns by their table's (schema, name); name-order within a table
|
|
25
41
|
// (matches the TypeScript generator's per-table `columns.sort(by name)`).
|
|
26
|
-
columns: [...metadata.columns].sort((a, b) => a.schema
|
|
27
|
-
a.table
|
|
28
|
-
a.name
|
|
42
|
+
columns: [...metadata.columns].sort((a, b) => compareStrings(a.schema, b.schema) ||
|
|
43
|
+
compareStrings(a.table, b.table) ||
|
|
44
|
+
compareStrings(a.name, b.name)),
|
|
29
45
|
// Grouped by table like `columns`; within a table the SQL's declared
|
|
30
46
|
// column order (`array_position(indkey, attnum)`) is preserved by the
|
|
31
47
|
// stable sort, since composite key order is meaningful.
|
|
32
|
-
primaryKeys: [...metadata.primaryKeys].sort((a, b) => a.schema
|
|
33
|
-
a.table_name
|
|
48
|
+
primaryKeys: [...metadata.primaryKeys].sort((a, b) => compareStrings(a.schema, b.schema) ||
|
|
49
|
+
compareStrings(a.table_name, b.table_name)),
|
|
34
50
|
relationships: [...metadata.relationships].sort(byRelationship),
|
|
35
51
|
// Functions can overload, so the signature is part of the identity. Args
|
|
36
52
|
// are addressed by name in generated RPC types, so sort them by name too.
|
|
37
53
|
functions: [...metadata.functions]
|
|
38
|
-
.sort((a, b) => a.schema
|
|
39
|
-
a.name
|
|
40
|
-
a.identity_argument_types
|
|
54
|
+
.sort((a, b) => compareStrings(a.schema, b.schema) ||
|
|
55
|
+
compareStrings(a.name, b.name) ||
|
|
56
|
+
compareStrings(a.identity_argument_types, b.identity_argument_types) ||
|
|
41
57
|
a.id - b.id)
|
|
42
58
|
.map((fn) => ({
|
|
43
59
|
...fn,
|
|
44
|
-
args: [...fn.args].sort((a, b) => a.name
|
|
60
|
+
args: [...fn.args].sort((a, b) => compareStrings(a.name, b.name)),
|
|
45
61
|
})),
|
|
46
62
|
types: [...metadata.types].sort(bySchemaName),
|
|
47
63
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -182,8 +182,8 @@ declare const postgresViewSchema: import("arktype/internal/variants/object.ts").
|
|
|
182
182
|
schema: string;
|
|
183
183
|
name: string;
|
|
184
184
|
is_updatable: boolean;
|
|
185
|
-
is_insert_enabled
|
|
186
|
-
is_update_enabled
|
|
185
|
+
is_insert_enabled?: boolean | undefined;
|
|
186
|
+
is_update_enabled?: boolean | undefined;
|
|
187
187
|
comment: string | null;
|
|
188
188
|
columns?: {
|
|
189
189
|
table_id: number;
|
|
@@ -252,9 +252,9 @@ export declare const generatorMetadataSchema: import("arktype/internal/variants/
|
|
|
252
252
|
views: {
|
|
253
253
|
comment: string | null;
|
|
254
254
|
id: number;
|
|
255
|
-
is_insert_enabled
|
|
255
|
+
is_insert_enabled?: boolean | undefined;
|
|
256
256
|
is_updatable: boolean;
|
|
257
|
-
is_update_enabled
|
|
257
|
+
is_update_enabled?: boolean | undefined;
|
|
258
258
|
name: string;
|
|
259
259
|
schema: string;
|
|
260
260
|
}[];
|
package/dist/types.js
CHANGED
|
@@ -139,12 +139,14 @@ const postgresViewSchema = type({
|
|
|
139
139
|
/**
|
|
140
140
|
* Whether INSERT works through this view: auto-updatable per
|
|
141
141
|
* `pg_relation_is_updatable`, or made insertable by an INSTEAD OF INSERT
|
|
142
|
-
* trigger. Tracked separately from
|
|
143
|
-
* `information_schema.views` and ignores
|
|
142
|
+
* trigger or an unconditional INSTEAD rule. Tracked separately from
|
|
143
|
+
* `is_updatable` (which mirrors `information_schema.views` and ignores
|
|
144
|
+
* triggers). Optional because version 1 metadata predating this field is
|
|
145
|
+
* still valid; consumers fall back to `is_updatable` when absent.
|
|
144
146
|
*/
|
|
145
|
-
is_insert_enabled: "boolean",
|
|
147
|
+
"is_insert_enabled?": "boolean",
|
|
146
148
|
/** Same as `is_insert_enabled`, for UPDATE. */
|
|
147
|
-
is_update_enabled: "boolean",
|
|
149
|
+
"is_update_enabled?": "boolean",
|
|
148
150
|
comment: "string | null",
|
|
149
151
|
"columns?": postgresColumnSchema.array(),
|
|
150
152
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supabase/postgrest-typegen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Type generation for PostgREST from PostgreSQL schemas",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codegen",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"arktype": "2.2.3",
|
|
65
|
-
"oxfmt": "0.
|
|
65
|
+
"oxfmt": "0.66.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@testcontainers/postgresql": "^12.1.0",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"@types/node": "^26.3.0",
|
|
73
73
|
"@types/pg": "^8.23.1",
|
|
74
74
|
"knip": "^6.32.3",
|
|
75
|
-
"oxlint": "1.
|
|
76
|
-
"oxlint-tsgolint": "7.0.
|
|
75
|
+
"oxlint": "1.83.0",
|
|
76
|
+
"oxlint-tsgolint": "7.0.2002",
|
|
77
77
|
"pg": "^8.23.0",
|
|
78
78
|
"testcontainers": "^12.1.0",
|
|
79
79
|
"typescript": "7.0.2"
|
package/src/collation.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one string comparison every ordering in this package uses.
|
|
3
|
+
*
|
|
4
|
+
* Generated output is ordered by name, and `String.prototype.localeCompare`
|
|
5
|
+
* without a locale follows the host's default locale, so the same database
|
|
6
|
+
* produced differently ordered code on differently configured machines (a
|
|
7
|
+
* Swedish host sorts `ä` after `z`, an English one next to `a`). Pinning the
|
|
8
|
+
* collator to English keeps the order postgres-meta's output has on the
|
|
9
|
+
* English CI runners and makes it the same everywhere else.
|
|
10
|
+
*/
|
|
11
|
+
const collator = new Intl.Collator("en");
|
|
12
|
+
|
|
13
|
+
/** Compare two strings with the pinned English collation. */
|
|
14
|
+
export function compareStrings(a: string, b: string): number {
|
|
15
|
+
return collator.compare(a, b);
|
|
16
|
+
}
|
package/src/generation/index.ts
CHANGED
package/src/generation/python.ts
CHANGED
|
@@ -88,7 +88,9 @@ class PythonContext {
|
|
|
88
88
|
this.schemas = Object.fromEntries(
|
|
89
89
|
schemas.map((schema) => [schema.name, schema]),
|
|
90
90
|
);
|
|
91
|
-
this.types = Object.fromEntries(
|
|
91
|
+
this.types = Object.fromEntries(
|
|
92
|
+
types.map((type) => [qualifiedTypeName(type.schema, type.name), type]),
|
|
93
|
+
);
|
|
92
94
|
this.columns = columns.reduce(
|
|
93
95
|
(acc, curr) => {
|
|
94
96
|
acc[curr.table_id] ??= [];
|
|
@@ -100,32 +102,37 @@ class PythonContext {
|
|
|
100
102
|
this.user_enums = Object.fromEntries(
|
|
101
103
|
types
|
|
102
104
|
.filter((type) => type.enums.length > 0)
|
|
103
|
-
.map((type) => [
|
|
105
|
+
.map((type) => [
|
|
106
|
+
qualifiedTypeName(type.schema, type.name),
|
|
107
|
+
new PythonEnum(type),
|
|
108
|
+
]),
|
|
104
109
|
);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
resolveTypeName(name: string): string {
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
resolveTypeName(name: string, typeSchema: string): string {
|
|
113
|
+
const qualified = qualifiedTypeName(typeSchema, name);
|
|
114
|
+
if (qualified in this.user_enums) {
|
|
115
|
+
return this.user_enums[qualified].name;
|
|
110
116
|
}
|
|
111
117
|
if (name in PY_TYPE_MAP) {
|
|
112
118
|
return PY_TYPE_MAP[name];
|
|
113
119
|
}
|
|
114
|
-
if (
|
|
115
|
-
const type = this.types[
|
|
116
|
-
|
|
117
|
-
return `${formatForPyClassName(schema)}${formatForPyClassName(type.name)}`;
|
|
120
|
+
if (qualified in this.types) {
|
|
121
|
+
const type = this.types[qualified];
|
|
122
|
+
return `${formatForPyClassName(type.schema)}${formatForPyClassName(type.name)}`;
|
|
118
123
|
}
|
|
119
124
|
return "Any";
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
// Array types live in the schema of their element type, so `typeSchema`
|
|
128
|
+
// carries over to the element.
|
|
129
|
+
parsePgType(pg_type: string, typeSchema: string): PythonType {
|
|
123
130
|
if (pg_type.startsWith("_")) {
|
|
124
131
|
const inner_str = pg_type.slice(1);
|
|
125
|
-
const inner = this.parsePgType(inner_str);
|
|
132
|
+
const inner = this.parsePgType(inner_str, typeSchema);
|
|
126
133
|
return new PythonListType(inner);
|
|
127
134
|
} else {
|
|
128
|
-
const type_name = this.resolveTypeName(pg_type);
|
|
135
|
+
const type_name = this.resolveTypeName(pg_type, typeSchema);
|
|
129
136
|
return new PythonSimpleType(type_name);
|
|
130
137
|
}
|
|
131
138
|
}
|
|
@@ -141,7 +148,10 @@ class PythonContext {
|
|
|
141
148
|
});
|
|
142
149
|
const attributeEntries: PythonBaseModelAttr[] = attributes.map(
|
|
143
150
|
(attribute) => {
|
|
144
|
-
const type = this.parsePgType(
|
|
151
|
+
const type = this.parsePgType(
|
|
152
|
+
attribute.type!.name,
|
|
153
|
+
attribute.type!.schema,
|
|
154
|
+
);
|
|
145
155
|
// Composite type attributes cannot carry NOT NULL constraints in
|
|
146
156
|
// Postgres, so every field is inherently nullable.
|
|
147
157
|
return new PythonBaseModelAttr(attribute.name, type, true);
|
|
@@ -155,7 +165,7 @@ class PythonContext {
|
|
|
155
165
|
columnsToClassAttrs(table_id: number): PythonBaseModelAttr[] {
|
|
156
166
|
const attrs = this.columns[table_id] ?? [];
|
|
157
167
|
return attrs.map((col) => {
|
|
158
|
-
const type = this.parsePgType(col.format);
|
|
168
|
+
const type = this.parsePgType(col.format, col.type_schema);
|
|
159
169
|
return new PythonBaseModelAttr(col.name, type, col.is_nullable);
|
|
160
170
|
});
|
|
161
171
|
}
|
|
@@ -166,7 +176,7 @@ class PythonContext {
|
|
|
166
176
|
): PythonTypedDictAttr[] {
|
|
167
177
|
const attrs = this.columns[table_id] ?? [];
|
|
168
178
|
return attrs.map((col) => {
|
|
169
|
-
const type = this.parsePgType(col.format);
|
|
179
|
+
const type = this.parsePgType(col.format, col.type_schema);
|
|
170
180
|
return new PythonTypedDictAttr(
|
|
171
181
|
col.name,
|
|
172
182
|
type,
|
|
@@ -222,6 +232,9 @@ class PythonContext {
|
|
|
222
232
|
}
|
|
223
233
|
}
|
|
224
234
|
|
|
235
|
+
const qualifiedTypeName = (schema: string, name: string): string =>
|
|
236
|
+
`${schema}.${name}`;
|
|
237
|
+
|
|
225
238
|
class PythonEnum implements Serializable {
|
|
226
239
|
name: string;
|
|
227
240
|
variants: string[];
|
package/src/generation/swift.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
PostgresType,
|
|
8
8
|
PostgresView,
|
|
9
9
|
} from "../types.ts";
|
|
10
|
+
import { compareStrings } from "../collation.ts";
|
|
10
11
|
|
|
11
12
|
type Operation = "Select" | "Insert" | "Update";
|
|
12
13
|
export type AccessControl = "internal" | "public" | "private" | "package";
|
|
@@ -313,11 +314,11 @@ export const generateSwift = (
|
|
|
313
314
|
...schemas.flatMap((schema) => {
|
|
314
315
|
const schemaTables = [...tables, ...foreignTables]
|
|
315
316
|
.filter((table) => table.schema === schema.name)
|
|
316
|
-
.sort(({ name: a }, { name: b }) => a
|
|
317
|
+
.sort(({ name: a }, { name: b }) => compareStrings(a, b));
|
|
317
318
|
|
|
318
319
|
const schemaViews = [...views, ...materializedViews]
|
|
319
320
|
.filter((table) => table.schema === schema.name)
|
|
320
|
-
.sort(({ name: a }, { name: b }) => a
|
|
321
|
+
.sort(({ name: a }, { name: b }) => compareStrings(a, b));
|
|
321
322
|
|
|
322
323
|
const schemaEnums = types.filter(
|
|
323
324
|
(type) => type.schema === schema.name && type.enums.length > 0,
|
|
@@ -424,6 +425,12 @@ const pgTypeToSwiftType = (
|
|
|
424
425
|
swiftType = "String";
|
|
425
426
|
} else if (["json", "jsonb"].includes(pgType)) {
|
|
426
427
|
swiftType = "AnyJSON";
|
|
428
|
+
// `void` and `record` are pseudo-types: Postgres rejects them as a column or
|
|
429
|
+
// composite attribute type ("column has pseudo-type void"), and this
|
|
430
|
+
// generator only renders columns and attributes, never functions. So these
|
|
431
|
+
// two arms are unreachable today and deliberately untested. They are kept
|
|
432
|
+
// because they are the mappings to use if Swift ever emits functions, which
|
|
433
|
+
// is where these types do appear.
|
|
427
434
|
} else if (pgType === "void") {
|
|
428
435
|
swiftType = "Void";
|
|
429
436
|
} else if (pgType === "record") {
|