@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
package/src/sort.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical ordering pass for {@link GeneratorMetadata}.
|
|
3
|
+
*
|
|
4
|
+
* The language generators consume the metadata collections in array order.
|
|
5
|
+
* This pass is the single, generator-agnostic stabilizer: it returns a new
|
|
6
|
+
* `GeneratorMetadata` whose every collection is ordered so that any producer
|
|
7
|
+
* (the bundled SQL `introspect()` or a custom injected adapter) yields
|
|
8
|
+
* byte-stable codegen. Generators document that they expect input pre-sorted
|
|
9
|
+
* with this function; ordering is intentionally NOT enforced inside
|
|
10
|
+
* `introspect()` so the concern lives in one place.
|
|
11
|
+
*
|
|
12
|
+
* **The TypeScript generator is the source of truth for ordering.** The keys
|
|
13
|
+
* below replicate the sorts that `generateTypescript` historically applied
|
|
14
|
+
* internally (schemas/tables/views/functions/types/columns/relationships by
|
|
15
|
+
* name; function args by name), so that generator can drop its own sorting and
|
|
16
|
+
* stay byte-identical. The other generators then inherit the same canonical
|
|
17
|
+
* order for free.
|
|
18
|
+
*
|
|
19
|
+
* Keys are **semantic** (schema + name + signature), never oid: equivalent
|
|
20
|
+
* databases assign different oids, so an oid sort would still churn output
|
|
21
|
+
* across environments. `id` is only a final tie-breaker for a total order.
|
|
22
|
+
*
|
|
23
|
+
* Composite type `attributes` and enum values are left untouched — their order
|
|
24
|
+
* is semantically meaningful (struct field / enum-label order) and must be
|
|
25
|
+
* preserved. Function `args` are sorted by name because PostgREST RPC args are
|
|
26
|
+
* addressed by name (the generated type is an object), matching TypeScript.
|
|
27
|
+
*/
|
|
28
|
+
import type { GeneratorMetadata, PostgresRelationship } from "./types.ts";
|
|
29
|
+
|
|
30
|
+
// Relations (tables/views/materialized views/foreign tables) and types share a
|
|
31
|
+
// (schema, name) identity within a database; `id` breaks any residual tie.
|
|
32
|
+
const bySchemaName = (
|
|
33
|
+
a: { schema: string; name: string; id: number },
|
|
34
|
+
b: { schema: string; name: string; id: number },
|
|
35
|
+
): number =>
|
|
36
|
+
a.schema.localeCompare(b.schema) ||
|
|
37
|
+
a.name.localeCompare(b.name) ||
|
|
38
|
+
a.id - b.id;
|
|
39
|
+
|
|
40
|
+
// Mirrors the TypeScript generator's historical `relationships.sort`.
|
|
41
|
+
const byRelationship = (
|
|
42
|
+
a: PostgresRelationship,
|
|
43
|
+
b: PostgresRelationship,
|
|
44
|
+
): number =>
|
|
45
|
+
a.foreign_key_name.localeCompare(b.foreign_key_name) ||
|
|
46
|
+
a.referenced_relation.localeCompare(b.referenced_relation) ||
|
|
47
|
+
JSON.stringify(a.referenced_columns).localeCompare(
|
|
48
|
+
JSON.stringify(b.referenced_columns),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Return a new {@link GeneratorMetadata} with every collection ordered by a
|
|
53
|
+
* stable, total, semantic key (matching the TypeScript generator's ordering).
|
|
54
|
+
* Pure (does not mutate the input) and idempotent. Apply this after
|
|
55
|
+
* introspection and before any `generate*` call.
|
|
56
|
+
*/
|
|
57
|
+
export function sortGeneratorMetadata(
|
|
58
|
+
metadata: GeneratorMetadata,
|
|
59
|
+
): GeneratorMetadata {
|
|
60
|
+
return {
|
|
61
|
+
version: metadata.version,
|
|
62
|
+
schemas: [...metadata.schemas].sort(
|
|
63
|
+
(a, b) => a.name.localeCompare(b.name) || a.id - b.id,
|
|
64
|
+
),
|
|
65
|
+
tables: [...metadata.tables].sort(bySchemaName),
|
|
66
|
+
foreignTables: [...metadata.foreignTables].sort(bySchemaName),
|
|
67
|
+
views: [...metadata.views].sort(bySchemaName),
|
|
68
|
+
materializedViews: [...metadata.materializedViews].sort(bySchemaName),
|
|
69
|
+
// Group columns by their table's (schema, name); name-order within a table
|
|
70
|
+
// (matches the TypeScript generator's per-table `columns.sort(by name)`).
|
|
71
|
+
columns: [...metadata.columns].sort(
|
|
72
|
+
(a, b) =>
|
|
73
|
+
a.schema.localeCompare(b.schema) ||
|
|
74
|
+
a.table.localeCompare(b.table) ||
|
|
75
|
+
a.name.localeCompare(b.name),
|
|
76
|
+
),
|
|
77
|
+
// Grouped by table like `columns`; within a table the SQL's declared
|
|
78
|
+
// column order (`array_position(indkey, attnum)`) is preserved by the
|
|
79
|
+
// stable sort, since composite key order is meaningful.
|
|
80
|
+
primaryKeys: [...metadata.primaryKeys].sort(
|
|
81
|
+
(a, b) =>
|
|
82
|
+
a.schema.localeCompare(b.schema) ||
|
|
83
|
+
a.table_name.localeCompare(b.table_name),
|
|
84
|
+
),
|
|
85
|
+
relationships: [...metadata.relationships].sort(byRelationship),
|
|
86
|
+
// Functions can overload, so the signature is part of the identity. Args
|
|
87
|
+
// are addressed by name in generated RPC types, so sort them by name too.
|
|
88
|
+
functions: [...metadata.functions]
|
|
89
|
+
.sort(
|
|
90
|
+
(a, b) =>
|
|
91
|
+
a.schema.localeCompare(b.schema) ||
|
|
92
|
+
a.name.localeCompare(b.name) ||
|
|
93
|
+
a.identity_argument_types.localeCompare(b.identity_argument_types) ||
|
|
94
|
+
a.id - b.id,
|
|
95
|
+
)
|
|
96
|
+
.map((fn) => ({
|
|
97
|
+
...fn,
|
|
98
|
+
args: [...fn.args].sort((a, b) => a.name.localeCompare(b.name)),
|
|
99
|
+
})),
|
|
100
|
+
types: [...metadata.types].sort(bySchemaName),
|
|
101
|
+
};
|
|
102
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public metadata contract for type generation.
|
|
3
|
+
*
|
|
4
|
+
* ArkType is the single source of truth: each `Postgres*` schema below is a
|
|
5
|
+
* runtime validator, and the corresponding exported type is derived from it via
|
|
6
|
+
* `.infer`. The shapes are kept identical to postgres-meta's `src/lib/types.ts`
|
|
7
|
+
* so that postgres-meta can consume this package as a drop-in replacement for
|
|
8
|
+
* its embedded templates (a compile-time equivalence test pins this).
|
|
9
|
+
*
|
|
10
|
+
* `GeneratorMetadata` is the pluggable contract: the SQL introspector is the
|
|
11
|
+
* default producer, but any source able to produce this shape can feed the
|
|
12
|
+
* language generators. Because the producer may be an injected adapter,
|
|
13
|
+
* `parseGeneratorMetadata` is offered so integrators can validate a result at
|
|
14
|
+
* runtime instead of blindly casting it. Validation is deliberately *not* baked
|
|
15
|
+
* into `introspect()` — it is an opt-in helper.
|
|
16
|
+
*/
|
|
17
|
+
import { type } from "arktype";
|
|
18
|
+
|
|
19
|
+
const postgresColumnSchema = type({
|
|
20
|
+
table_id: "number",
|
|
21
|
+
schema: "string",
|
|
22
|
+
table: "string",
|
|
23
|
+
/** `<table_id>.<ordinal_position>` */
|
|
24
|
+
id: "string",
|
|
25
|
+
ordinal_position: "number",
|
|
26
|
+
name: "string",
|
|
27
|
+
default_value: "unknown",
|
|
28
|
+
data_type: "string",
|
|
29
|
+
format: "string",
|
|
30
|
+
/**
|
|
31
|
+
* Schema owning `format`'s type. Bare enum/composite/domain names are
|
|
32
|
+
* ambiguous across schemas (two schemas can each define an enum named
|
|
33
|
+
* `mood`); this disambiguates without changing `format` itself.
|
|
34
|
+
*/
|
|
35
|
+
type_schema: "string",
|
|
36
|
+
is_identity: "boolean",
|
|
37
|
+
identity_generation: "'ALWAYS' | 'BY DEFAULT' | null",
|
|
38
|
+
is_generated: "boolean",
|
|
39
|
+
is_nullable: "boolean",
|
|
40
|
+
is_updatable: "boolean",
|
|
41
|
+
is_unique: "boolean",
|
|
42
|
+
enums: "string[]",
|
|
43
|
+
check: "string | null",
|
|
44
|
+
comment: "string | null",
|
|
45
|
+
});
|
|
46
|
+
export type PostgresColumn = typeof postgresColumnSchema.infer;
|
|
47
|
+
|
|
48
|
+
const postgresForeignTableSchema = type({
|
|
49
|
+
id: "number",
|
|
50
|
+
schema: "string",
|
|
51
|
+
name: "string",
|
|
52
|
+
comment: "string | null",
|
|
53
|
+
"columns?": postgresColumnSchema.array(),
|
|
54
|
+
});
|
|
55
|
+
export type PostgresForeignTable = typeof postgresForeignTableSchema.infer;
|
|
56
|
+
|
|
57
|
+
const postgresFunctionSchema = type({
|
|
58
|
+
id: "number",
|
|
59
|
+
schema: "string",
|
|
60
|
+
name: "string",
|
|
61
|
+
language: "string",
|
|
62
|
+
definition: "string",
|
|
63
|
+
complete_statement: "string",
|
|
64
|
+
args: type({
|
|
65
|
+
mode: "'in' | 'out' | 'inout' | 'variadic' | 'table'",
|
|
66
|
+
name: "string",
|
|
67
|
+
type_id: "number",
|
|
68
|
+
// `introspect()` emits `null` for the OUT columns of RETURNS TABLE /
|
|
69
|
+
// OUT-arg functions: the introspection SQL sizes `arg_has_defaults` from
|
|
70
|
+
// the input-arg count while `arg_modes`/`arg_types` include output args,
|
|
71
|
+
// so the trailing rows are padded with NULL. postgres-meta types this as a
|
|
72
|
+
// plain boolean, but the opt-in validator must accept the real output.
|
|
73
|
+
has_default: "boolean | null",
|
|
74
|
+
}).array(),
|
|
75
|
+
argument_types: "string",
|
|
76
|
+
identity_argument_types: "string",
|
|
77
|
+
return_type_id: "number",
|
|
78
|
+
return_type: "string",
|
|
79
|
+
return_type_relation_id: "number | null",
|
|
80
|
+
is_set_returning_function: "boolean",
|
|
81
|
+
prorows: "number | null",
|
|
82
|
+
behavior: "'IMMUTABLE' | 'STABLE' | 'VOLATILE'",
|
|
83
|
+
security_definer: "boolean",
|
|
84
|
+
config_params: type({ "[string]": "string" }).or("null"),
|
|
85
|
+
});
|
|
86
|
+
export type PostgresFunction = typeof postgresFunctionSchema.infer;
|
|
87
|
+
|
|
88
|
+
const postgresMaterializedViewSchema = type({
|
|
89
|
+
id: "number",
|
|
90
|
+
schema: "string",
|
|
91
|
+
name: "string",
|
|
92
|
+
is_populated: "boolean",
|
|
93
|
+
comment: "string | null",
|
|
94
|
+
"columns?": postgresColumnSchema.array(),
|
|
95
|
+
});
|
|
96
|
+
export type PostgresMaterializedView =
|
|
97
|
+
typeof postgresMaterializedViewSchema.infer;
|
|
98
|
+
|
|
99
|
+
const postgresPrimaryKeySchema = type({
|
|
100
|
+
schema: "string",
|
|
101
|
+
table_name: "string",
|
|
102
|
+
name: "string",
|
|
103
|
+
table_id: "number",
|
|
104
|
+
});
|
|
105
|
+
export type PostgresPrimaryKey = typeof postgresPrimaryKeySchema.infer;
|
|
106
|
+
|
|
107
|
+
const postgresRelationshipSchema = type({
|
|
108
|
+
foreign_key_name: "string",
|
|
109
|
+
schema: "string",
|
|
110
|
+
relation: "string",
|
|
111
|
+
columns: "string[]",
|
|
112
|
+
is_one_to_one: "boolean",
|
|
113
|
+
referenced_schema: "string",
|
|
114
|
+
referenced_relation: "string",
|
|
115
|
+
referenced_columns: "string[]",
|
|
116
|
+
});
|
|
117
|
+
export type PostgresRelationship = typeof postgresRelationshipSchema.infer;
|
|
118
|
+
|
|
119
|
+
const postgresSchemaSchema = type({
|
|
120
|
+
id: "number",
|
|
121
|
+
name: "string",
|
|
122
|
+
owner: "string",
|
|
123
|
+
});
|
|
124
|
+
export type PostgresSchema = typeof postgresSchemaSchema.infer;
|
|
125
|
+
|
|
126
|
+
const postgresTableSchema = type({
|
|
127
|
+
id: "number",
|
|
128
|
+
schema: "string",
|
|
129
|
+
name: "string",
|
|
130
|
+
rls_enabled: "boolean",
|
|
131
|
+
rls_forced: "boolean",
|
|
132
|
+
replica_identity: "'DEFAULT' | 'INDEX' | 'FULL' | 'NOTHING'",
|
|
133
|
+
bytes: "number",
|
|
134
|
+
size: "string",
|
|
135
|
+
live_rows_estimate: "number",
|
|
136
|
+
dead_rows_estimate: "number",
|
|
137
|
+
comment: "string | null",
|
|
138
|
+
"columns?": postgresColumnSchema.array(),
|
|
139
|
+
});
|
|
140
|
+
export type PostgresTable = typeof postgresTableSchema.infer;
|
|
141
|
+
|
|
142
|
+
const postgresTypeSchema = type({
|
|
143
|
+
id: "number",
|
|
144
|
+
name: "string",
|
|
145
|
+
schema: "string",
|
|
146
|
+
format: "string",
|
|
147
|
+
enums: "string[]",
|
|
148
|
+
attributes: type({ name: "string", type_id: "number" }).array(),
|
|
149
|
+
comment: "string | null",
|
|
150
|
+
type_relation_id: "number | null",
|
|
151
|
+
});
|
|
152
|
+
export type PostgresType = typeof postgresTypeSchema.infer;
|
|
153
|
+
|
|
154
|
+
const postgresViewSchema = type({
|
|
155
|
+
id: "number",
|
|
156
|
+
schema: "string",
|
|
157
|
+
name: "string",
|
|
158
|
+
is_updatable: "boolean",
|
|
159
|
+
comment: "string | null",
|
|
160
|
+
"columns?": postgresColumnSchema.array(),
|
|
161
|
+
});
|
|
162
|
+
export type PostgresView = typeof postgresViewSchema.infer;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Bumped whenever `GeneratorMetadata`'s shape changes in a way a consumer
|
|
166
|
+
* should branch on. Out-of-process consumers (a generator in another
|
|
167
|
+
* language's SDK repo, fed JSON over a process boundary) only see the
|
|
168
|
+
* serialized document, so a shape change is otherwise undetectable until
|
|
169
|
+
* something breaks at read time.
|
|
170
|
+
*/
|
|
171
|
+
export const GENERATOR_METADATA_VERSION = 1;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The complete set of introspected metadata required to generate types.
|
|
175
|
+
* Produced by `introspect()` (the default SQL-based producer) or any other
|
|
176
|
+
* source able to satisfy this shape. The table-like collections drop the
|
|
177
|
+
* optional `columns` field (mirroring `Omit<…, "columns">`); columns are
|
|
178
|
+
* carried separately on `columns`.
|
|
179
|
+
*/
|
|
180
|
+
export const generatorMetadataSchema = type({
|
|
181
|
+
version: `${GENERATOR_METADATA_VERSION}`,
|
|
182
|
+
schemas: postgresSchemaSchema.array(),
|
|
183
|
+
tables: postgresTableSchema.omit("columns").array(),
|
|
184
|
+
foreignTables: postgresForeignTableSchema.omit("columns").array(),
|
|
185
|
+
views: postgresViewSchema.omit("columns").array(),
|
|
186
|
+
materializedViews: postgresMaterializedViewSchema.omit("columns").array(),
|
|
187
|
+
columns: postgresColumnSchema.array(),
|
|
188
|
+
primaryKeys: postgresPrimaryKeySchema.array(),
|
|
189
|
+
relationships: postgresRelationshipSchema.array(),
|
|
190
|
+
functions: postgresFunctionSchema.array(),
|
|
191
|
+
types: postgresTypeSchema.array(),
|
|
192
|
+
});
|
|
193
|
+
export type GeneratorMetadata = typeof generatorMetadataSchema.infer;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* JSON Schema for {@link generatorMetadataSchema}, published so out-of-process
|
|
197
|
+
* consumers (a generator living in its own SDK repo, not this package) can
|
|
198
|
+
* validate or generate types against the contract without depending on
|
|
199
|
+
* ArkType or this package's TypeScript types.
|
|
200
|
+
*/
|
|
201
|
+
export const generatorMetadataJsonSchema =
|
|
202
|
+
generatorMetadataSchema.toJsonSchema();
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Serialize a {@link GeneratorMetadata} value to the JSON document that
|
|
206
|
+
* crosses the process boundary to an out-of-process generator (e.g. `dart
|
|
207
|
+
* run supabase_typegen`). `GeneratorMetadata` is plain, JSON-safe data, so
|
|
208
|
+
* this is a thin, explicit boundary rather than a transform, kept as a named
|
|
209
|
+
* export so every caller serializes the contract the same way.
|
|
210
|
+
*/
|
|
211
|
+
export function serializeGeneratorMetadata(
|
|
212
|
+
metadata: GeneratorMetadata,
|
|
213
|
+
): string {
|
|
214
|
+
return JSON.stringify(metadata);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Validate an unknown value against the {@link generatorMetadataSchema} and
|
|
219
|
+
* return it typed as {@link GeneratorMetadata}. Throws a `TypeError` with
|
|
220
|
+
* ArkType's human-readable summary if validation fails.
|
|
221
|
+
*
|
|
222
|
+
* Offered as an opt-in helper so any integrator producing `GeneratorMetadata`
|
|
223
|
+
* through an injected adapter can verify the result at runtime instead of
|
|
224
|
+
* casting. `introspect()` does not call this — wrap its result yourself when
|
|
225
|
+
* you want the guarantee:
|
|
226
|
+
*
|
|
227
|
+
* ```ts
|
|
228
|
+
* const metadata = parseGeneratorMetadata(await introspect(db));
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export function parseGeneratorMetadata(data: unknown): GeneratorMetadata {
|
|
232
|
+
const out = generatorMetadataSchema(data);
|
|
233
|
+
if (out instanceof type.errors) {
|
|
234
|
+
throw new TypeError(`Invalid GeneratorMetadata: ${out.summary}`);
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|