@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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/generation/constants.d.ts +9 -0
  4. package/dist/generation/constants.js +10 -0
  5. package/dist/generation/go.d.ts +6 -0
  6. package/dist/generation/go.js +233 -0
  7. package/dist/generation/index.d.ts +5 -0
  8. package/dist/generation/index.js +5 -0
  9. package/dist/generation/python.d.ts +6 -0
  10. package/dist/generation/python.js +339 -0
  11. package/dist/generation/swift.d.ts +11 -0
  12. package/dist/generation/swift.js +309 -0
  13. package/dist/generation/typescript.d.ts +32 -0
  14. package/dist/generation/typescript.js +796 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/introspection/index.d.ts +27 -0
  18. package/dist/introspection/index.js +70 -0
  19. package/dist/introspection/normalize.d.ts +31 -0
  20. package/dist/introspection/normalize.js +65 -0
  21. package/dist/introspection/relationships.d.ts +47 -0
  22. package/dist/introspection/relationships.js +85 -0
  23. package/dist/introspection/sql/columns.sql.d.ts +7 -0
  24. package/dist/introspection/sql/columns.sql.js +122 -0
  25. package/dist/introspection/sql/common.d.ts +16 -0
  26. package/dist/introspection/sql/common.js +1 -0
  27. package/dist/introspection/sql/foreign_tables.sql.d.ts +6 -0
  28. package/dist/introspection/sql/foreign_tables.sql.js +18 -0
  29. package/dist/introspection/sql/functions.sql.d.ts +5 -0
  30. package/dist/introspection/sql/functions.sql.js +148 -0
  31. package/dist/introspection/sql/helpers.d.ts +12 -0
  32. package/dist/introspection/sql/helpers.js +28 -0
  33. package/dist/introspection/sql/materialized_views.sql.d.ts +4 -0
  34. package/dist/introspection/sql/materialized_views.sql.js +19 -0
  35. package/dist/introspection/sql/primary_keys.sql.d.ts +4 -0
  36. package/dist/introspection/sql/primary_keys.sql.js +32 -0
  37. package/dist/introspection/sql/schemas.sql.d.ts +6 -0
  38. package/dist/introspection/sql/schemas.sql.js +24 -0
  39. package/dist/introspection/sql/table.sql.d.ts +4 -0
  40. package/dist/introspection/sql/table.sql.js +41 -0
  41. package/dist/introspection/sql/table_relationships.sql.d.ts +2 -0
  42. package/dist/introspection/sql/table_relationships.sql.js +49 -0
  43. package/dist/introspection/sql/types.sql.d.ts +5 -0
  44. package/dist/introspection/sql/types.sql.js +65 -0
  45. package/dist/introspection/sql/views.sql.d.ts +4 -0
  46. package/dist/introspection/sql/views.sql.js +20 -0
  47. package/dist/introspection/sql/views_key_dependencies.sql.d.ts +2 -0
  48. package/dist/introspection/sql/views_key_dependencies.sql.js +195 -0
  49. package/dist/sort.d.ts +35 -0
  50. package/dist/sort.js +48 -0
  51. package/dist/types.d.ts +372 -0
  52. package/dist/types.js +207 -0
  53. package/package.json +87 -0
  54. package/src/generation/constants.ts +11 -0
  55. package/src/generation/go.ts +356 -0
  56. package/src/generation/index.ts +13 -0
  57. package/src/generation/python.ts +461 -0
  58. package/src/generation/swift.ts +481 -0
  59. package/src/generation/typescript.ts +1144 -0
  60. package/src/index.ts +4 -0
  61. package/src/introspection/index.ts +124 -0
  62. package/src/introspection/normalize.ts +71 -0
  63. package/src/introspection/relationships.ts +173 -0
  64. package/src/introspection/sql/columns.sql.ts +131 -0
  65. package/src/introspection/sql/common.ts +18 -0
  66. package/src/introspection/sql/foreign_tables.sql.ts +26 -0
  67. package/src/introspection/sql/functions.sql.ts +159 -0
  68. package/src/introspection/sql/helpers.ts +33 -0
  69. package/src/introspection/sql/materialized_views.sql.ts +25 -0
  70. package/src/introspection/sql/primary_keys.sql.ts +38 -0
  71. package/src/introspection/sql/schemas.sql.ts +32 -0
  72. package/src/introspection/sql/table.sql.ts +47 -0
  73. package/src/introspection/sql/table_relationships.sql.ts +53 -0
  74. package/src/introspection/sql/types.sql.ts +74 -0
  75. package/src/introspection/sql/views.sql.ts +26 -0
  76. package/src/introspection/sql/views_key_dependencies.sql.ts +199 -0
  77. package/src/sort.ts +102 -0
  78. package/src/types.ts +237 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Emits tables/views/materialized views in `GeneratorMetadata` order. Pass
3
+ * input pre-sorted with `sortGeneratorMetadata` for deterministic output.
4
+ */
5
+ export const generatePython = ({ schemas, tables, views, materializedViews, columns, types, }) => {
6
+ const ctx = new PythonContext(types, columns, schemas);
7
+ // Used for efficient lookup of types by schema name
8
+ const schemasNames = new Set(schemas.map((schema) => schema.name));
9
+ const py_tables = tables.flatMap((table) => {
10
+ const py_class_and_methods = ctx.tableToClass(table);
11
+ return py_class_and_methods;
12
+ });
13
+ const composite_types = types
14
+ // We always include system schemas, so we need to filter out types that are not in the included schemas
15
+ .filter((type) => type.attributes.length > 0 && schemasNames.has(type.schema))
16
+ .map((type) => ctx.typeToClass(type));
17
+ const py_views = views.map((view) => ctx.viewToClass(view));
18
+ const py_matviews = materializedViews.map((matview) => ctx.matViewToClass(matview));
19
+ let output = `
20
+ from __future__ import annotations
21
+
22
+ import datetime
23
+ import uuid
24
+ from typing import (
25
+ Annotated,
26
+ Any,
27
+ List,
28
+ Literal,
29
+ NotRequired,
30
+ Optional,
31
+ TypeAlias,
32
+ TypedDict,
33
+ )
34
+
35
+ from pydantic import BaseModel, Field, Json
36
+
37
+ ${concatLines(Object.values(ctx.user_enums))}
38
+
39
+ ${concatLines(py_tables)}
40
+
41
+ ${concatLines(py_views)}
42
+
43
+ ${concatLines(py_matviews)}
44
+
45
+ ${concatLines(composite_types)}
46
+
47
+ `.trim();
48
+ return output;
49
+ };
50
+ class PythonContext {
51
+ types;
52
+ user_enums;
53
+ columns;
54
+ schemas;
55
+ constructor(types, columns, schemas) {
56
+ this.schemas = Object.fromEntries(schemas.map((schema) => [schema.name, schema]));
57
+ this.types = Object.fromEntries(types.map((type) => [type.name, type]));
58
+ this.columns = columns.reduce((acc, curr) => {
59
+ acc[curr.table_id] ??= [];
60
+ acc[curr.table_id].push(curr);
61
+ return acc;
62
+ }, {});
63
+ this.user_enums = Object.fromEntries(types
64
+ .filter((type) => type.enums.length > 0)
65
+ .map((type) => [type.name, new PythonEnum(type)]));
66
+ }
67
+ resolveTypeName(name) {
68
+ if (name in this.user_enums) {
69
+ return this.user_enums[name].name;
70
+ }
71
+ if (name in PY_TYPE_MAP) {
72
+ return PY_TYPE_MAP[name];
73
+ }
74
+ if (name in this.types) {
75
+ const type = this.types[name];
76
+ const schema = type.schema;
77
+ return `${formatForPyClassName(schema)}${formatForPyClassName(type.name)}`;
78
+ }
79
+ return "Any";
80
+ }
81
+ parsePgType(pg_type) {
82
+ if (pg_type.startsWith("_")) {
83
+ const inner_str = pg_type.slice(1);
84
+ const inner = this.parsePgType(inner_str);
85
+ return new PythonListType(inner);
86
+ }
87
+ else {
88
+ const type_name = this.resolveTypeName(pg_type);
89
+ return new PythonSimpleType(type_name);
90
+ }
91
+ }
92
+ typeToClass(type) {
93
+ const types = Object.values(this.types);
94
+ const attributes = type.attributes.map((attribute) => {
95
+ const type = types.find((type) => type.id === attribute.type_id);
96
+ return {
97
+ ...attribute,
98
+ type,
99
+ };
100
+ });
101
+ const attributeEntries = attributes.map((attribute) => {
102
+ const type = this.parsePgType(attribute.type.name);
103
+ return new PythonBaseModelAttr(attribute.name, type, false);
104
+ });
105
+ const schema = this.schemas[type.schema];
106
+ return new PythonBaseModel(type.name, schema, attributeEntries);
107
+ }
108
+ columnsToClassAttrs(table_id) {
109
+ const attrs = this.columns[table_id] ?? [];
110
+ return attrs.map((col) => {
111
+ const type = this.parsePgType(col.format);
112
+ return new PythonBaseModelAttr(col.name, type, col.is_nullable);
113
+ });
114
+ }
115
+ columnsToDictAttrs(table_id, not_required) {
116
+ const attrs = this.columns[table_id] ?? [];
117
+ return attrs.map((col) => {
118
+ const type = this.parsePgType(col.format);
119
+ return new PythonTypedDictAttr(col.name, type, col.is_nullable, not_required ||
120
+ col.is_nullable ||
121
+ col.is_identity ||
122
+ col.default_value !== null);
123
+ });
124
+ }
125
+ tableToClass(table) {
126
+ const schema = this.schemas[table.schema];
127
+ const select = new PythonBaseModel(table.name, schema, this.columnsToClassAttrs(table.id));
128
+ const insert = new PythonTypedDict(table.name, "Insert", schema, this.columnsToDictAttrs(table.id, false));
129
+ const update = new PythonTypedDict(table.name, "Update", schema, this.columnsToDictAttrs(table.id, true));
130
+ return [select, insert, update];
131
+ }
132
+ viewToClass(view) {
133
+ const attributes = this.columnsToClassAttrs(view.id);
134
+ return new PythonBaseModel(view.name, this.schemas[view.schema], attributes);
135
+ }
136
+ matViewToClass(matview) {
137
+ const attributes = this.columnsToClassAttrs(matview.id);
138
+ return new PythonBaseModel(matview.name, this.schemas[matview.schema], attributes);
139
+ }
140
+ }
141
+ class PythonEnum {
142
+ name;
143
+ variants;
144
+ constructor(type) {
145
+ this.name = `${formatForPyClassName(type.schema)}${formatForPyClassName(type.name)}`;
146
+ this.variants = type.enums;
147
+ }
148
+ serialize() {
149
+ const variants = this.variants.map((item) => `"${item}"`).join(", ");
150
+ return `${this.name}: TypeAlias = Literal[${variants}]`;
151
+ }
152
+ }
153
+ class PythonSimpleType {
154
+ name;
155
+ constructor(name) {
156
+ this.name = name;
157
+ }
158
+ serialize() {
159
+ return this.name;
160
+ }
161
+ }
162
+ class PythonListType {
163
+ inner;
164
+ constructor(inner) {
165
+ this.inner = inner;
166
+ }
167
+ serialize() {
168
+ return `List[${this.inner.serialize()}]`;
169
+ }
170
+ }
171
+ class PythonBaseModelAttr {
172
+ name;
173
+ pg_name;
174
+ py_type;
175
+ nullable;
176
+ constructor(name, py_type, nullable) {
177
+ this.name = formatForPyAttributeName(name);
178
+ this.pg_name = name;
179
+ this.py_type = py_type;
180
+ this.nullable = nullable;
181
+ }
182
+ serialize() {
183
+ const py_type = this.nullable
184
+ ? `Optional[${this.py_type.serialize()}]`
185
+ : this.py_type.serialize();
186
+ return ` ${this.name}: ${py_type} = Field(alias="${this.pg_name}")`;
187
+ }
188
+ }
189
+ class PythonBaseModel {
190
+ name;
191
+ table_name;
192
+ schema;
193
+ class_attributes;
194
+ constructor(name, schema, class_attributes) {
195
+ this.schema = schema;
196
+ this.class_attributes = class_attributes;
197
+ this.table_name = name;
198
+ this.name = `${formatForPyClassName(schema.name)}${formatForPyClassName(name)}`;
199
+ }
200
+ serialize() {
201
+ const attributes = this.class_attributes.length > 0
202
+ ? this.class_attributes.map((attr) => attr.serialize()).join("\n")
203
+ : " pass";
204
+ return `class ${this.name}(BaseModel):\n${attributes}`;
205
+ }
206
+ }
207
+ class PythonTypedDictAttr {
208
+ name;
209
+ pg_name;
210
+ py_type;
211
+ nullable;
212
+ not_required;
213
+ constructor(name, py_type, nullable, required) {
214
+ this.name = formatForPyAttributeName(name);
215
+ this.pg_name = name;
216
+ this.py_type = py_type;
217
+ this.nullable = nullable;
218
+ this.not_required = required;
219
+ }
220
+ serialize() {
221
+ const py_type = this.nullable
222
+ ? `Optional[${this.py_type.serialize()}]`
223
+ : this.py_type.serialize();
224
+ const annotation = `Annotated[${py_type}, Field(alias="${this.pg_name}")]`;
225
+ const rhs = this.not_required ? `NotRequired[${annotation}]` : annotation;
226
+ return ` ${this.name}: ${rhs}`;
227
+ }
228
+ }
229
+ class PythonTypedDict {
230
+ name;
231
+ table_name;
232
+ parent_class;
233
+ schema;
234
+ dict_attributes;
235
+ operation;
236
+ constructor(name, operation, schema, dict_attributes, parent_class = "BaseModel") {
237
+ this.schema = schema;
238
+ this.dict_attributes = dict_attributes;
239
+ this.table_name = name;
240
+ this.name = `${formatForPyClassName(schema.name)}${formatForPyClassName(name)}`;
241
+ this.parent_class = parent_class;
242
+ this.operation = operation;
243
+ }
244
+ serialize() {
245
+ const attributes = this.dict_attributes.length > 0
246
+ ? this.dict_attributes.map((attr) => attr.serialize()).join("\n")
247
+ : " pass";
248
+ return `class ${this.name}${this.operation}(TypedDict):\n${attributes}`;
249
+ }
250
+ }
251
+ function concatLines(items) {
252
+ return items.map((item) => item.serialize()).join("\n\n");
253
+ }
254
+ const PY_TYPE_MAP = {
255
+ // Bool
256
+ bool: "bool",
257
+ // Numbers
258
+ int2: "int",
259
+ int4: "int",
260
+ int8: "int",
261
+ float4: "float",
262
+ float8: "float",
263
+ numeric: "float",
264
+ // Strings
265
+ bytea: "bytes",
266
+ bpchar: "str",
267
+ varchar: "str",
268
+ string: "str",
269
+ date: "datetime.date",
270
+ text: "str",
271
+ citext: "str",
272
+ time: "datetime.time",
273
+ timetz: "datetime.time",
274
+ timestamp: "datetime.datetime",
275
+ timestamptz: "datetime.datetime",
276
+ uuid: "uuid.UUID",
277
+ vector: "list[Any]",
278
+ interval: "str",
279
+ // JSON
280
+ json: "Json[Any]",
281
+ jsonb: "Json[Any]",
282
+ // Range types (can be adjusted to more complex types if needed)
283
+ int4range: "str",
284
+ int4multirange: "str",
285
+ int8range: "str",
286
+ int8multirange: "str",
287
+ numrange: "str",
288
+ nummultirange: "str",
289
+ tsrange: "str",
290
+ tsmultirange: "str",
291
+ tstzrange: "str",
292
+ tstzmultirange: "str",
293
+ daterange: "str",
294
+ datemultirange: "str",
295
+ // Miscellaneous types
296
+ void: "None",
297
+ record: "dict[str, Any]",
298
+ };
299
+ /**
300
+ * Converts a Postgres name to PascalCase.
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * formatForPyTypeName('pokedex') // Pokedex
305
+ * formatForPyTypeName('pokemon_center') // PokemonCenter
306
+ * formatForPyTypeName('victory-road') // VictoryRoad
307
+ * formatForPyTypeName('pokemon league') // PokemonLeague
308
+ * ```
309
+ */
310
+ function formatForPyClassName(name) {
311
+ return name
312
+ .split(/[^a-zA-Z0-9]/)
313
+ .map((word) => {
314
+ if (word) {
315
+ return `${word[0].toUpperCase()}${word.slice(1)}`;
316
+ }
317
+ else {
318
+ return "";
319
+ }
320
+ })
321
+ .join("");
322
+ }
323
+ /**
324
+ * Converts a Postgres name to snake_case.
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * formatForPyTypeName('Pokedex') // pokedex
329
+ * formatForPyTypeName('PokemonCenter') // pokemon_enter
330
+ * formatForPyTypeName('victory-road') // victory_road
331
+ * formatForPyTypeName('pokemon league') // pokemon_league
332
+ * ```
333
+ */
334
+ function formatForPyAttributeName(name) {
335
+ return name
336
+ .split(/[^a-zA-Z0-9]+/) // Split on non-alphanumeric characters (like spaces, dashes, etc.)
337
+ .map((word) => word.toLowerCase()) // Convert each word to lowercase
338
+ .join("_"); // Join with underscores
339
+ }
@@ -0,0 +1,11 @@
1
+ import type { GeneratorMetadata } from "../types.ts";
2
+ export type AccessControl = "internal" | "public" | "private" | "package";
3
+ export interface GenerateSwiftOptions {
4
+ /** Swift access-control level applied to generated declarations. Default `'internal'`. */
5
+ accessControl?: AccessControl;
6
+ }
7
+ /**
8
+ * Emits tables/views/materialized views in `GeneratorMetadata` order. Pass
9
+ * input pre-sorted with `sortGeneratorMetadata` for deterministic output.
10
+ */
11
+ export declare const generateSwift: (metadata: GeneratorMetadata, opts?: GenerateSwiftOptions) => string;
@@ -0,0 +1,309 @@
1
+ function formatForSwiftSchemaName(schema) {
2
+ return `${formatForSwiftTypeName(schema)}Schema`;
3
+ }
4
+ function pgEnumToSwiftEnum(pgEnum) {
5
+ return {
6
+ formattedEnumName: formatForSwiftTypeName(pgEnum.name),
7
+ protocolConformances: ["String", "Codable", "Hashable", "Sendable"],
8
+ cases: pgEnum.enums.map((case_) => {
9
+ return {
10
+ formattedName: formatForSwiftPropertyName(case_),
11
+ rawValue: case_,
12
+ };
13
+ }),
14
+ };
15
+ }
16
+ function pgTypeToSwiftStruct(table, columns, operation, { types, views, tables, }) {
17
+ const columnEntries = columns?.map((column) => {
18
+ let nullable;
19
+ if (operation === "Insert") {
20
+ nullable =
21
+ column.is_nullable ||
22
+ column.is_identity ||
23
+ column.is_generated ||
24
+ !!column.default_value;
25
+ }
26
+ else if (operation === "Update") {
27
+ nullable = true;
28
+ }
29
+ else {
30
+ nullable = column.is_nullable;
31
+ }
32
+ return {
33
+ rawName: column.name,
34
+ formattedAttributeName: formatForSwiftPropertyName(column.name),
35
+ formattedType: pgTypeToSwiftType(column.format, nullable, {
36
+ types,
37
+ views,
38
+ tables,
39
+ }),
40
+ isIdentity: column.is_identity,
41
+ };
42
+ }) ?? [];
43
+ return {
44
+ formattedStructName: `${formatForSwiftTypeName(table.name)}${operation}`,
45
+ attributes: columnEntries,
46
+ protocolConformances: ["Codable", "Hashable", "Sendable"],
47
+ codingKeysEnum: generateCodingKeysEnumFromAttributes(columnEntries),
48
+ };
49
+ }
50
+ function generateCodingKeysEnumFromAttributes(attributes) {
51
+ return attributes.length > 0
52
+ ? {
53
+ formattedEnumName: "CodingKeys",
54
+ protocolConformances: ["String", "CodingKey"],
55
+ cases: attributes.map((attribute) => {
56
+ return {
57
+ formattedName: attribute.formattedAttributeName,
58
+ rawValue: attribute.rawName,
59
+ };
60
+ }),
61
+ }
62
+ : undefined;
63
+ }
64
+ function pgCompositeTypeToSwiftStruct(type, { types, views, tables, }) {
65
+ const typeWithRetrievedAttributes = {
66
+ ...type,
67
+ attributes: type.attributes.map((attribute) => {
68
+ const type = types.find((type) => type.id === attribute.type_id);
69
+ return {
70
+ ...attribute,
71
+ type,
72
+ };
73
+ }),
74
+ };
75
+ const attributeEntries = typeWithRetrievedAttributes.attributes.map((attribute) => {
76
+ return {
77
+ formattedAttributeName: formatForSwiftTypeName(attribute.name),
78
+ formattedType: pgTypeToSwiftType(attribute.type.format, false, {
79
+ types,
80
+ views,
81
+ tables,
82
+ }),
83
+ rawName: attribute.name,
84
+ isIdentity: false,
85
+ };
86
+ });
87
+ return {
88
+ formattedStructName: formatForSwiftTypeName(type.name),
89
+ attributes: attributeEntries,
90
+ protocolConformances: ["Codable", "Hashable", "Sendable"],
91
+ codingKeysEnum: generateCodingKeysEnumFromAttributes(attributeEntries),
92
+ };
93
+ }
94
+ function generateProtocolConformances(protocols) {
95
+ return protocols.length === 0 ? "" : `: ${protocols.join(", ")}`;
96
+ }
97
+ function generateEnum(enum_, { accessControl, level }) {
98
+ return [
99
+ `${ident(level)}${accessControl} enum ${enum_.formattedEnumName}${generateProtocolConformances(enum_.protocolConformances)} {`,
100
+ ...enum_.cases.map((case_) => `${ident(level + 1)}case ${case_.formattedName} = "${case_.rawValue}"`),
101
+ `${ident(level)}}`,
102
+ ];
103
+ }
104
+ function generateStruct(struct, { accessControl, level }) {
105
+ const identity = struct.attributes.find((column) => column.isIdentity);
106
+ let protocolConformances = struct.protocolConformances;
107
+ if (identity) {
108
+ protocolConformances.push("Identifiable");
109
+ }
110
+ let output = [
111
+ `${ident(level)}${accessControl} struct ${struct.formattedStructName}${generateProtocolConformances(struct.protocolConformances)} {`,
112
+ ];
113
+ if (identity && identity.formattedAttributeName !== "id") {
114
+ output.push(`${ident(level + 1)}${accessControl} var id: ${identity.formattedType} { ${identity.formattedAttributeName} }`);
115
+ }
116
+ output.push(...struct.attributes.map((attribute) => `${ident(level + 1)}${accessControl} let ${attribute.formattedAttributeName}: ${attribute.formattedType}`));
117
+ if (struct.codingKeysEnum) {
118
+ output.push(...generateEnum(struct.codingKeysEnum, {
119
+ accessControl,
120
+ level: level + 1,
121
+ }));
122
+ }
123
+ output.push(`${ident(level)}}`);
124
+ return output;
125
+ }
126
+ /**
127
+ * Emits tables/views/materialized views in `GeneratorMetadata` order. Pass
128
+ * input pre-sorted with `sortGeneratorMetadata` for deterministic output.
129
+ */
130
+ export const generateSwift = (metadata, opts = {}) => {
131
+ const { schemas, tables, foreignTables, views, materializedViews, columns, types, } = metadata;
132
+ const { accessControl = "internal" } = opts;
133
+ const columnsByTableId = Object.fromEntries([...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [
134
+ t.id,
135
+ [],
136
+ ]));
137
+ columns
138
+ .filter((c) => c.table_id in columnsByTableId)
139
+ .forEach((c) => columnsByTableId[c.table_id].push(c));
140
+ let output = [
141
+ "import Foundation",
142
+ "import Supabase",
143
+ "",
144
+ ...schemas.flatMap((schema) => {
145
+ const schemaTables = [...tables, ...foreignTables]
146
+ .filter((table) => table.schema === schema.name)
147
+ .sort(({ name: a }, { name: b }) => a.localeCompare(b));
148
+ const schemaViews = [...views, ...materializedViews]
149
+ .filter((table) => table.schema === schema.name)
150
+ .sort(({ name: a }, { name: b }) => a.localeCompare(b));
151
+ const schemaEnums = types.filter((type) => type.schema === schema.name && type.enums.length > 0);
152
+ const schemaCompositeTypes = types.filter((type) => type.schema === schema.name && type.attributes.length > 0);
153
+ return [
154
+ `${accessControl} enum ${formatForSwiftSchemaName(schema.name)} {`,
155
+ ...schemaEnums.flatMap((enum_) => generateEnum(pgEnumToSwiftEnum(enum_), { accessControl, level: 1 })),
156
+ ...schemaTables.flatMap((table) => ["Select", "Insert", "Update"]
157
+ .map((operation) => pgTypeToSwiftStruct(table, columnsByTableId[table.id], operation, {
158
+ types,
159
+ views,
160
+ tables,
161
+ }))
162
+ .flatMap((struct) => generateStruct(struct, { accessControl, level: 1 }))),
163
+ ...schemaViews.flatMap((view) => generateStruct(pgTypeToSwiftStruct(view, columnsByTableId[view.id], "Select", {
164
+ types,
165
+ views,
166
+ tables,
167
+ }), { accessControl, level: 1 })),
168
+ ...schemaCompositeTypes.flatMap((type) => generateStruct(pgCompositeTypeToSwiftStruct(type, { types, views, tables }), {
169
+ accessControl,
170
+ level: 1,
171
+ })),
172
+ "}",
173
+ ];
174
+ }),
175
+ ];
176
+ return output.join("\n");
177
+ };
178
+ // TODO: Make this more robust. Currently doesn't handle range types - returns them as string.
179
+ const pgTypeToSwiftType = (pgType, nullable, { types, views, tables, }) => {
180
+ let swiftType;
181
+ if (pgType === "bool") {
182
+ swiftType = "Bool";
183
+ }
184
+ else if (pgType === "int2") {
185
+ swiftType = "Int16";
186
+ }
187
+ else if (pgType === "int4") {
188
+ swiftType = "Int32";
189
+ }
190
+ else if (pgType === "int8") {
191
+ swiftType = "Int64";
192
+ }
193
+ else if (pgType === "float4") {
194
+ swiftType = "Float";
195
+ }
196
+ else if (pgType === "float8") {
197
+ swiftType = "Double";
198
+ }
199
+ else if (["numeric", "decimal"].includes(pgType)) {
200
+ swiftType = "Decimal";
201
+ }
202
+ else if (pgType === "uuid") {
203
+ swiftType = "UUID";
204
+ }
205
+ else if ([
206
+ "bytea",
207
+ "bpchar",
208
+ "varchar",
209
+ "date",
210
+ "text",
211
+ "citext",
212
+ "time",
213
+ "timetz",
214
+ "timestamp",
215
+ "timestamptz",
216
+ "interval",
217
+ "vector",
218
+ ].includes(pgType)) {
219
+ swiftType = "String";
220
+ }
221
+ else if (["json", "jsonb"].includes(pgType)) {
222
+ swiftType = "AnyJSON";
223
+ }
224
+ else if (pgType === "void") {
225
+ swiftType = "Void";
226
+ }
227
+ else if (pgType === "record") {
228
+ swiftType = "JSONObject";
229
+ }
230
+ else if (pgType.startsWith("_")) {
231
+ swiftType = `[${pgTypeToSwiftType(pgType.substring(1), false, { types, views, tables })}]`;
232
+ }
233
+ else {
234
+ const enumType = types.find((type) => type.name === pgType && type.enums.length > 0);
235
+ const compositeTypes = [...types, ...views, ...tables].find((type) => type.name === pgType);
236
+ if (enumType) {
237
+ swiftType = `${formatForSwiftTypeName(enumType.name)}`;
238
+ }
239
+ else if (compositeTypes) {
240
+ // Append a `Select` to the composite type, as that is how is named in the generated struct.
241
+ swiftType = `${formatForSwiftTypeName(compositeTypes.name)}Select`;
242
+ }
243
+ else {
244
+ swiftType = "AnyJSON";
245
+ }
246
+ }
247
+ return `${swiftType}${nullable ? "?" : ""}`;
248
+ };
249
+ function ident(level, options = { width: 2 }) {
250
+ return " ".repeat(level * options.width);
251
+ }
252
+ /**
253
+ * Converts a Postgres name to PascalCase.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * formatForSwiftTypeName('pokedex') // Pokedex
258
+ * formatForSwiftTypeName('pokemon_center') // PokemonCenter
259
+ * formatForSwiftTypeName('victory-road') // VictoryRoad
260
+ * formatForSwiftTypeName('pokemon league') // PokemonLeague
261
+ * formatForSwiftTypeName('_key_id_context') // _KeyIdContext
262
+ * ```
263
+ */
264
+ function formatForSwiftTypeName(name) {
265
+ // Preserve the initial underscore if it exists
266
+ let prefix = "";
267
+ if (name.startsWith("_")) {
268
+ prefix = "_";
269
+ name = name.slice(1); // Remove the initial underscore for processing
270
+ }
271
+ return (prefix +
272
+ name
273
+ .split(/[^a-zA-Z0-9]+/)
274
+ .map((word) => {
275
+ if (word) {
276
+ return `${word[0].toUpperCase()}${word.slice(1)}`;
277
+ }
278
+ else {
279
+ return "";
280
+ }
281
+ })
282
+ .join(""));
283
+ }
284
+ const SWIFT_KEYWORDS = ["in", "default", "case"];
285
+ /**
286
+ * Converts a Postgres name to pascalCase.
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * formatForSwiftTypeName('pokedex') // pokedex
291
+ * formatForSwiftTypeName('pokemon_center') // pokemonCenter
292
+ * formatForSwiftTypeName('victory-road') // victoryRoad
293
+ * formatForSwiftTypeName('pokemon league') // pokemonLeague
294
+ * ```
295
+ */
296
+ function formatForSwiftPropertyName(name) {
297
+ const propertyName = name
298
+ .split(/[^a-zA-Z0-9]/)
299
+ .map((word, index) => {
300
+ const lowerWord = word.toLowerCase();
301
+ return index !== 0
302
+ ? lowerWord.charAt(0).toUpperCase() + lowerWord.slice(1)
303
+ : lowerWord;
304
+ })
305
+ .join("");
306
+ return SWIFT_KEYWORDS.includes(propertyName)
307
+ ? `\`${propertyName}\``
308
+ : propertyName;
309
+ }
@@ -0,0 +1,32 @@
1
+ import type { GeneratorMetadata, PostgresSchema, PostgresTable, PostgresType, PostgresView } from "../types.ts";
2
+ export interface GenerateTypescriptOptions {
3
+ /**
4
+ * Emit `isOneToOne` on each relationship entry. Mirrors postgres-meta's
5
+ * `detect_one_to_one_relationships` query flag. Default `false`.
6
+ */
7
+ detectOneToOneRelationships?: boolean;
8
+ /**
9
+ * When set, emit `__InternalSupabase.PostgrestVersion` so `createClient`
10
+ * can be instantiated with the right options automatically.
11
+ */
12
+ postgrestVersion?: string;
13
+ /**
14
+ * Schema treated as the default for the generated `Tables`/`Enums`/etc.
15
+ * helper types. Replaces postgres-meta's `GENERATE_TYPES_DEFAULT_SCHEMA`
16
+ * env read. Default `'public'`.
17
+ */
18
+ defaultSchema?: string;
19
+ }
20
+ /**
21
+ * Does not re-sort collections; pass input pre-sorted with
22
+ * `sortGeneratorMetadata` first. Still merge-sorts tables+foreign tables and
23
+ * views+materialized views per schema, since that cross-collection grouping
24
+ * can't be expressed by the single-collection `sortGeneratorMetadata` pass.
25
+ */
26
+ export declare const generateTypescript: (metadata: GeneratorMetadata, opts?: GenerateTypescriptOptions) => Promise<string>;
27
+ export declare const pgTypeToTsType: (schema: PostgresSchema, pgType: string, { types, schemas, tables, views, }: {
28
+ types: PostgresType[];
29
+ schemas: PostgresSchema[];
30
+ tables: PostgresTable[];
31
+ views: PostgresView[];
32
+ }, typeSchema?: string) => string;