@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,481 @@
1
+ import type {
2
+ GeneratorMetadata,
3
+ PostgresColumn,
4
+ PostgresForeignTable,
5
+ PostgresMaterializedView,
6
+ PostgresTable,
7
+ PostgresType,
8
+ PostgresView,
9
+ } from "../types.ts";
10
+
11
+ type Operation = "Select" | "Insert" | "Update";
12
+ export type AccessControl = "internal" | "public" | "private" | "package";
13
+
14
+ export interface GenerateSwiftOptions {
15
+ /** Swift access-control level applied to generated declarations. Default `'internal'`. */
16
+ accessControl?: AccessControl;
17
+ }
18
+
19
+ type SwiftGeneratorOptions = {
20
+ accessControl: AccessControl;
21
+ };
22
+
23
+ type SwiftEnumCase = {
24
+ formattedName: string;
25
+ rawValue: string;
26
+ };
27
+
28
+ type SwiftEnum = {
29
+ formattedEnumName: string;
30
+ protocolConformances: string[];
31
+ cases: SwiftEnumCase[];
32
+ };
33
+
34
+ type SwiftAttribute = {
35
+ formattedAttributeName: string;
36
+ formattedType: string;
37
+ rawName: string;
38
+ isIdentity: boolean;
39
+ };
40
+
41
+ type SwiftStruct = {
42
+ formattedStructName: string;
43
+ protocolConformances: string[];
44
+ attributes: SwiftAttribute[];
45
+ codingKeysEnum: SwiftEnum | undefined;
46
+ };
47
+
48
+ function formatForSwiftSchemaName(schema: string): string {
49
+ return `${formatForSwiftTypeName(schema)}Schema`;
50
+ }
51
+
52
+ function pgEnumToSwiftEnum(pgEnum: PostgresType): SwiftEnum {
53
+ return {
54
+ formattedEnumName: formatForSwiftTypeName(pgEnum.name),
55
+ protocolConformances: ["String", "Codable", "Hashable", "Sendable"],
56
+ cases: pgEnum.enums.map((case_) => {
57
+ return {
58
+ formattedName: formatForSwiftPropertyName(case_),
59
+ rawValue: case_,
60
+ };
61
+ }),
62
+ };
63
+ }
64
+
65
+ function pgTypeToSwiftStruct(
66
+ table:
67
+ | PostgresTable
68
+ | PostgresForeignTable
69
+ | PostgresView
70
+ | PostgresMaterializedView,
71
+ columns: PostgresColumn[] | undefined,
72
+ operation: Operation,
73
+ {
74
+ types,
75
+ views,
76
+ tables,
77
+ }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] },
78
+ ): SwiftStruct {
79
+ const columnEntries: SwiftAttribute[] =
80
+ columns?.map((column) => {
81
+ let nullable: boolean;
82
+
83
+ if (operation === "Insert") {
84
+ nullable =
85
+ column.is_nullable ||
86
+ column.is_identity ||
87
+ column.is_generated ||
88
+ !!column.default_value;
89
+ } else if (operation === "Update") {
90
+ nullable = true;
91
+ } else {
92
+ nullable = column.is_nullable;
93
+ }
94
+
95
+ return {
96
+ rawName: column.name,
97
+ formattedAttributeName: formatForSwiftPropertyName(column.name),
98
+ formattedType: pgTypeToSwiftType(column.format, nullable, {
99
+ types,
100
+ views,
101
+ tables,
102
+ }),
103
+ isIdentity: column.is_identity,
104
+ };
105
+ }) ?? [];
106
+
107
+ return {
108
+ formattedStructName: `${formatForSwiftTypeName(table.name)}${operation}`,
109
+ attributes: columnEntries,
110
+ protocolConformances: ["Codable", "Hashable", "Sendable"],
111
+ codingKeysEnum: generateCodingKeysEnumFromAttributes(columnEntries),
112
+ };
113
+ }
114
+
115
+ function generateCodingKeysEnumFromAttributes(
116
+ attributes: SwiftAttribute[],
117
+ ): SwiftEnum | undefined {
118
+ return attributes.length > 0
119
+ ? {
120
+ formattedEnumName: "CodingKeys",
121
+ protocolConformances: ["String", "CodingKey"],
122
+ cases: attributes.map((attribute) => {
123
+ return {
124
+ formattedName: attribute.formattedAttributeName,
125
+ rawValue: attribute.rawName,
126
+ };
127
+ }),
128
+ }
129
+ : undefined;
130
+ }
131
+
132
+ function pgCompositeTypeToSwiftStruct(
133
+ type: PostgresType,
134
+ {
135
+ types,
136
+ views,
137
+ tables,
138
+ }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] },
139
+ ): SwiftStruct {
140
+ const typeWithRetrievedAttributes = {
141
+ ...type,
142
+ attributes: type.attributes.map((attribute) => {
143
+ const type = types.find((type) => type.id === attribute.type_id);
144
+ return {
145
+ ...attribute,
146
+ type,
147
+ };
148
+ }),
149
+ };
150
+
151
+ const attributeEntries: SwiftAttribute[] =
152
+ typeWithRetrievedAttributes.attributes.map((attribute) => {
153
+ return {
154
+ formattedAttributeName: formatForSwiftTypeName(attribute.name),
155
+ formattedType: pgTypeToSwiftType(attribute.type!.format, false, {
156
+ types,
157
+ views,
158
+ tables,
159
+ }),
160
+ rawName: attribute.name,
161
+ isIdentity: false,
162
+ };
163
+ });
164
+
165
+ return {
166
+ formattedStructName: formatForSwiftTypeName(type.name),
167
+ attributes: attributeEntries,
168
+ protocolConformances: ["Codable", "Hashable", "Sendable"],
169
+ codingKeysEnum: generateCodingKeysEnumFromAttributes(attributeEntries),
170
+ };
171
+ }
172
+
173
+ function generateProtocolConformances(protocols: string[]): string {
174
+ return protocols.length === 0 ? "" : `: ${protocols.join(", ")}`;
175
+ }
176
+
177
+ function generateEnum(
178
+ enum_: SwiftEnum,
179
+ { accessControl, level }: SwiftGeneratorOptions & { level: number },
180
+ ): string[] {
181
+ return [
182
+ `${ident(level)}${accessControl} enum ${enum_.formattedEnumName}${generateProtocolConformances(
183
+ enum_.protocolConformances,
184
+ )} {`,
185
+ ...enum_.cases.map(
186
+ (case_) =>
187
+ `${ident(level + 1)}case ${case_.formattedName} = "${case_.rawValue}"`,
188
+ ),
189
+ `${ident(level)}}`,
190
+ ];
191
+ }
192
+
193
+ function generateStruct(
194
+ struct: SwiftStruct,
195
+ { accessControl, level }: SwiftGeneratorOptions & { level: number },
196
+ ): string[] {
197
+ const identity = struct.attributes.find((column) => column.isIdentity);
198
+
199
+ let protocolConformances = struct.protocolConformances;
200
+ if (identity) {
201
+ protocolConformances.push("Identifiable");
202
+ }
203
+
204
+ let output = [
205
+ `${ident(level)}${accessControl} struct ${struct.formattedStructName}${generateProtocolConformances(
206
+ struct.protocolConformances,
207
+ )} {`,
208
+ ];
209
+
210
+ if (identity && identity.formattedAttributeName !== "id") {
211
+ output.push(
212
+ `${ident(level + 1)}${accessControl} var id: ${identity.formattedType} { ${identity.formattedAttributeName} }`,
213
+ );
214
+ }
215
+
216
+ output.push(
217
+ ...struct.attributes.map(
218
+ (attribute) =>
219
+ `${ident(level + 1)}${accessControl} let ${attribute.formattedAttributeName}: ${attribute.formattedType}`,
220
+ ),
221
+ );
222
+
223
+ if (struct.codingKeysEnum) {
224
+ output.push(
225
+ ...generateEnum(struct.codingKeysEnum, {
226
+ accessControl,
227
+ level: level + 1,
228
+ }),
229
+ );
230
+ }
231
+
232
+ output.push(`${ident(level)}}`);
233
+
234
+ return output;
235
+ }
236
+
237
+ /**
238
+ * Emits tables/views/materialized views in `GeneratorMetadata` order. Pass
239
+ * input pre-sorted with `sortGeneratorMetadata` for deterministic output.
240
+ */
241
+ export const generateSwift = (
242
+ metadata: GeneratorMetadata,
243
+ opts: GenerateSwiftOptions = {},
244
+ ): string => {
245
+ const {
246
+ schemas,
247
+ tables,
248
+ foreignTables,
249
+ views,
250
+ materializedViews,
251
+ columns,
252
+ types,
253
+ } = metadata;
254
+ const { accessControl = "internal" } = opts;
255
+ const columnsByTableId = Object.fromEntries<PostgresColumn[]>(
256
+ [...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [
257
+ t.id,
258
+ [],
259
+ ]),
260
+ );
261
+
262
+ columns
263
+ .filter((c) => c.table_id in columnsByTableId)
264
+ .forEach((c) => columnsByTableId[c.table_id].push(c));
265
+
266
+ let output = [
267
+ "import Foundation",
268
+ "import Supabase",
269
+ "",
270
+ ...schemas.flatMap((schema) => {
271
+ const schemaTables = [...tables, ...foreignTables]
272
+ .filter((table) => table.schema === schema.name)
273
+ .sort(({ name: a }, { name: b }) => a.localeCompare(b));
274
+
275
+ const schemaViews = [...views, ...materializedViews]
276
+ .filter((table) => table.schema === schema.name)
277
+ .sort(({ name: a }, { name: b }) => a.localeCompare(b));
278
+
279
+ const schemaEnums = types.filter(
280
+ (type) => type.schema === schema.name && type.enums.length > 0,
281
+ );
282
+
283
+ const schemaCompositeTypes = types.filter(
284
+ (type) => type.schema === schema.name && type.attributes.length > 0,
285
+ );
286
+
287
+ return [
288
+ `${accessControl} enum ${formatForSwiftSchemaName(schema.name)} {`,
289
+ ...schemaEnums.flatMap((enum_) =>
290
+ generateEnum(pgEnumToSwiftEnum(enum_), { accessControl, level: 1 }),
291
+ ),
292
+ ...schemaTables.flatMap((table) =>
293
+ (["Select", "Insert", "Update"] as Operation[])
294
+ .map((operation) =>
295
+ pgTypeToSwiftStruct(
296
+ table,
297
+ columnsByTableId[table.id],
298
+ operation,
299
+ {
300
+ types,
301
+ views,
302
+ tables,
303
+ },
304
+ ),
305
+ )
306
+ .flatMap((struct) =>
307
+ generateStruct(struct, { accessControl, level: 1 }),
308
+ ),
309
+ ),
310
+ ...schemaViews.flatMap((view) =>
311
+ generateStruct(
312
+ pgTypeToSwiftStruct(view, columnsByTableId[view.id], "Select", {
313
+ types,
314
+ views,
315
+ tables,
316
+ }),
317
+ { accessControl, level: 1 },
318
+ ),
319
+ ),
320
+ ...schemaCompositeTypes.flatMap((type) =>
321
+ generateStruct(
322
+ pgCompositeTypeToSwiftStruct(type, { types, views, tables }),
323
+ {
324
+ accessControl,
325
+ level: 1,
326
+ },
327
+ ),
328
+ ),
329
+ "}",
330
+ ];
331
+ }),
332
+ ];
333
+
334
+ return output.join("\n");
335
+ };
336
+
337
+ // TODO: Make this more robust. Currently doesn't handle range types - returns them as string.
338
+ const pgTypeToSwiftType = (
339
+ pgType: string,
340
+ nullable: boolean,
341
+ {
342
+ types,
343
+ views,
344
+ tables,
345
+ }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] },
346
+ ): string => {
347
+ let swiftType: string;
348
+
349
+ if (pgType === "bool") {
350
+ swiftType = "Bool";
351
+ } else if (pgType === "int2") {
352
+ swiftType = "Int16";
353
+ } else if (pgType === "int4") {
354
+ swiftType = "Int32";
355
+ } else if (pgType === "int8") {
356
+ swiftType = "Int64";
357
+ } else if (pgType === "float4") {
358
+ swiftType = "Float";
359
+ } else if (pgType === "float8") {
360
+ swiftType = "Double";
361
+ } else if (["numeric", "decimal"].includes(pgType)) {
362
+ swiftType = "Decimal";
363
+ } else if (pgType === "uuid") {
364
+ swiftType = "UUID";
365
+ } else if (
366
+ [
367
+ "bytea",
368
+ "bpchar",
369
+ "varchar",
370
+ "date",
371
+ "text",
372
+ "citext",
373
+ "time",
374
+ "timetz",
375
+ "timestamp",
376
+ "timestamptz",
377
+ "interval",
378
+ "vector",
379
+ ].includes(pgType)
380
+ ) {
381
+ swiftType = "String";
382
+ } else if (["json", "jsonb"].includes(pgType)) {
383
+ swiftType = "AnyJSON";
384
+ } else if (pgType === "void") {
385
+ swiftType = "Void";
386
+ } else if (pgType === "record") {
387
+ swiftType = "JSONObject";
388
+ } else if (pgType.startsWith("_")) {
389
+ swiftType = `[${pgTypeToSwiftType(pgType.substring(1), false, { types, views, tables })}]`;
390
+ } else {
391
+ const enumType = types.find(
392
+ (type) => type.name === pgType && type.enums.length > 0,
393
+ );
394
+
395
+ const compositeTypes = [...types, ...views, ...tables].find(
396
+ (type) => type.name === pgType,
397
+ );
398
+
399
+ if (enumType) {
400
+ swiftType = `${formatForSwiftTypeName(enumType.name)}`;
401
+ } else if (compositeTypes) {
402
+ // Append a `Select` to the composite type, as that is how is named in the generated struct.
403
+ swiftType = `${formatForSwiftTypeName(compositeTypes.name)}Select`;
404
+ } else {
405
+ swiftType = "AnyJSON";
406
+ }
407
+ }
408
+
409
+ return `${swiftType}${nullable ? "?" : ""}`;
410
+ };
411
+
412
+ function ident(
413
+ level: number,
414
+ options: { width: number } = { width: 2 },
415
+ ): string {
416
+ return " ".repeat(level * options.width);
417
+ }
418
+
419
+ /**
420
+ * Converts a Postgres name to PascalCase.
421
+ *
422
+ * @example
423
+ * ```ts
424
+ * formatForSwiftTypeName('pokedex') // Pokedex
425
+ * formatForSwiftTypeName('pokemon_center') // PokemonCenter
426
+ * formatForSwiftTypeName('victory-road') // VictoryRoad
427
+ * formatForSwiftTypeName('pokemon league') // PokemonLeague
428
+ * formatForSwiftTypeName('_key_id_context') // _KeyIdContext
429
+ * ```
430
+ */
431
+ function formatForSwiftTypeName(name: string): string {
432
+ // Preserve the initial underscore if it exists
433
+ let prefix = "";
434
+ if (name.startsWith("_")) {
435
+ prefix = "_";
436
+ name = name.slice(1); // Remove the initial underscore for processing
437
+ }
438
+
439
+ return (
440
+ prefix +
441
+ name
442
+ .split(/[^a-zA-Z0-9]+/)
443
+ .map((word) => {
444
+ if (word) {
445
+ return `${word[0].toUpperCase()}${word.slice(1)}`;
446
+ } else {
447
+ return "";
448
+ }
449
+ })
450
+ .join("")
451
+ );
452
+ }
453
+
454
+ const SWIFT_KEYWORDS = ["in", "default", "case"];
455
+
456
+ /**
457
+ * Converts a Postgres name to pascalCase.
458
+ *
459
+ * @example
460
+ * ```ts
461
+ * formatForSwiftTypeName('pokedex') // pokedex
462
+ * formatForSwiftTypeName('pokemon_center') // pokemonCenter
463
+ * formatForSwiftTypeName('victory-road') // victoryRoad
464
+ * formatForSwiftTypeName('pokemon league') // pokemonLeague
465
+ * ```
466
+ */
467
+ function formatForSwiftPropertyName(name: string): string {
468
+ const propertyName = name
469
+ .split(/[^a-zA-Z0-9]/)
470
+ .map((word, index) => {
471
+ const lowerWord = word.toLowerCase();
472
+ return index !== 0
473
+ ? lowerWord.charAt(0).toUpperCase() + lowerWord.slice(1)
474
+ : lowerWord;
475
+ })
476
+ .join("");
477
+
478
+ return SWIFT_KEYWORDS.includes(propertyName)
479
+ ? `\`${propertyName}\``
480
+ : propertyName;
481
+ }