@supabase/postgrest-typegen 0.3.1 → 0.5.0

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.
@@ -1,358 +0,0 @@
1
- import { compareStrings } from "../collation.js";
2
- function formatForSwiftSchemaName(schema) {
3
- return `${formatForSwiftTypeName(schema)}Schema`;
4
- }
5
- function pgEnumToSwiftEnum(pgEnum) {
6
- return {
7
- formattedEnumName: formatForSwiftTypeName(pgEnum.name),
8
- protocolConformances: ["String", "Codable", "Hashable", "Sendable"],
9
- cases: pgEnum.enums.map((case_) => {
10
- return {
11
- formattedName: formatForSwiftPropertyName(case_),
12
- rawValue: case_,
13
- };
14
- }),
15
- };
16
- }
17
- function pgTypeToSwiftStruct(table, columns, operation, { types, views, tables, }) {
18
- const columnEntries = columns?.map((column) => {
19
- let nullable;
20
- if (operation === "Insert") {
21
- nullable =
22
- column.is_nullable ||
23
- column.is_identity ||
24
- column.is_generated ||
25
- !!column.default_value;
26
- }
27
- else if (operation === "Update") {
28
- nullable = true;
29
- }
30
- else {
31
- nullable = column.is_nullable;
32
- }
33
- return {
34
- rawName: column.name,
35
- formattedAttributeName: formatForSwiftPropertyName(column.name),
36
- formattedType: pgTypeToSwiftType(column.format, nullable, {
37
- types,
38
- views,
39
- tables,
40
- }),
41
- isIdentity: column.is_identity,
42
- };
43
- }) ?? [];
44
- return {
45
- formattedStructName: `${formatForSwiftTypeName(table.name)}${operation}`,
46
- attributes: columnEntries,
47
- protocolConformances: ["Codable", "Hashable", "Sendable"],
48
- codingKeysEnum: generateCodingKeysEnumFromAttributes(columnEntries),
49
- };
50
- }
51
- function generateCodingKeysEnumFromAttributes(attributes) {
52
- return attributes.length > 0
53
- ? {
54
- formattedEnumName: "CodingKeys",
55
- protocolConformances: ["String", "CodingKey"],
56
- cases: attributes.map((attribute) => {
57
- return {
58
- formattedName: attribute.formattedAttributeName,
59
- rawValue: attribute.rawName,
60
- };
61
- }),
62
- }
63
- : undefined;
64
- }
65
- function pgCompositeTypeToSwiftStruct(type, { types, views, tables, }) {
66
- const typeWithRetrievedAttributes = {
67
- ...type,
68
- attributes: type.attributes.map((attribute) => {
69
- const type = types.find((type) => type.id === attribute.type_id);
70
- return {
71
- ...attribute,
72
- type,
73
- };
74
- }),
75
- };
76
- const attributeEntries = typeWithRetrievedAttributes.attributes.map((attribute) => {
77
- return {
78
- formattedAttributeName: formatForSwiftTypeName(attribute.name),
79
- formattedType: pgTypeToSwiftType(attribute.type.format, false, {
80
- types,
81
- views,
82
- tables,
83
- }),
84
- rawName: attribute.name,
85
- isIdentity: false,
86
- };
87
- });
88
- return {
89
- formattedStructName: formatForSwiftTypeName(type.name),
90
- attributes: attributeEntries,
91
- protocolConformances: ["Codable", "Hashable", "Sendable"],
92
- codingKeysEnum: generateCodingKeysEnumFromAttributes(attributeEntries),
93
- };
94
- }
95
- function generateProtocolConformances(protocols) {
96
- return protocols.length === 0 ? "" : `: ${protocols.join(", ")}`;
97
- }
98
- /**
99
- * Serializes a database-provided value as a Swift string literal. Backslashes
100
- * would otherwise start escape or interpolation sequences (`\(`), double
101
- * quotes would terminate the literal early, and line breaks or control
102
- * characters are not allowed in single-line literals, so all of them are
103
- * escaped per Swift's string literal grammar. U+2028 and U+2029 are legal in
104
- * Swift literals but are escaped anyway because other tooling mishandles
105
- * them. Swift unescapes at compile time, so raw values round-trip unchanged.
106
- */
107
- function swiftStringLiteral(value) {
108
- let escaped = "";
109
- for (const character of value) {
110
- switch (character) {
111
- case "\\":
112
- escaped += "\\\\";
113
- break;
114
- case '"':
115
- escaped += '\\"';
116
- break;
117
- case "\t":
118
- escaped += "\\t";
119
- break;
120
- case "\n":
121
- escaped += "\\n";
122
- break;
123
- case "\r":
124
- escaped += "\\r";
125
- break;
126
- default: {
127
- const codePoint = character.codePointAt(0);
128
- escaped +=
129
- codePoint < 0x20 ||
130
- codePoint === 0x7f ||
131
- codePoint === 0x2028 ||
132
- codePoint === 0x2029
133
- ? `\\u{${codePoint.toString(16)}}`
134
- : character;
135
- }
136
- }
137
- }
138
- return `"${escaped}"`;
139
- }
140
- function generateEnum(enum_, { accessControl, level }) {
141
- return [
142
- `${ident(level)}${accessControl} enum ${enum_.formattedEnumName}${generateProtocolConformances(enum_.protocolConformances)} {`,
143
- ...enum_.cases.map((case_) => `${ident(level + 1)}case ${case_.formattedName} = ${swiftStringLiteral(case_.rawValue)}`),
144
- `${ident(level)}}`,
145
- ];
146
- }
147
- function generateStruct(struct, { accessControl, level }) {
148
- const identity = struct.attributes.find((column) => column.isIdentity);
149
- let protocolConformances = struct.protocolConformances;
150
- if (identity) {
151
- protocolConformances.push("Identifiable");
152
- }
153
- let output = [
154
- `${ident(level)}${accessControl} struct ${struct.formattedStructName}${generateProtocolConformances(struct.protocolConformances)} {`,
155
- ];
156
- if (identity && identity.formattedAttributeName !== "id") {
157
- output.push(`${ident(level + 1)}${accessControl} var id: ${identity.formattedType} { ${identity.formattedAttributeName} }`);
158
- }
159
- output.push(...struct.attributes.map((attribute) => `${ident(level + 1)}${accessControl} let ${attribute.formattedAttributeName}: ${attribute.formattedType}`));
160
- if (struct.codingKeysEnum) {
161
- output.push(...generateEnum(struct.codingKeysEnum, {
162
- accessControl,
163
- level: level + 1,
164
- }));
165
- }
166
- output.push(`${ident(level)}}`);
167
- return output;
168
- }
169
- /**
170
- * Emits tables/views/materialized views in `GeneratorMetadata` order. Pass
171
- * input pre-sorted with `sortGeneratorMetadata` for deterministic output.
172
- */
173
- export const generateSwift = (metadata, opts = {}) => {
174
- const { schemas, tables, foreignTables, views, materializedViews, columns, types, } = metadata;
175
- const { accessControl = "internal" } = opts;
176
- const columnsByTableId = Object.fromEntries([...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [
177
- t.id,
178
- [],
179
- ]));
180
- for (const column of columns) {
181
- columnsByTableId[column.table_id]?.push(column);
182
- }
183
- let output = [
184
- "import Foundation",
185
- "import Supabase",
186
- "",
187
- ...schemas.flatMap((schema) => {
188
- const schemaTables = [...tables, ...foreignTables]
189
- .filter((table) => table.schema === schema.name)
190
- .sort(({ name: a }, { name: b }) => compareStrings(a, b));
191
- const schemaViews = [...views, ...materializedViews]
192
- .filter((table) => table.schema === schema.name)
193
- .sort(({ name: a }, { name: b }) => compareStrings(a, b));
194
- const schemaEnums = types.filter((type) => type.schema === schema.name && type.enums.length > 0);
195
- const schemaCompositeTypes = types.filter((type) => type.schema === schema.name && type.attributes.length > 0);
196
- return [
197
- `${accessControl} enum ${formatForSwiftSchemaName(schema.name)} {`,
198
- ...schemaEnums.flatMap((enum_) => generateEnum(pgEnumToSwiftEnum(enum_), { accessControl, level: 1 })),
199
- ...schemaTables.flatMap((table) => ["Select", "Insert", "Update"]
200
- .map((operation) => pgTypeToSwiftStruct(table, columnsByTableId[table.id], operation, {
201
- types,
202
- views,
203
- tables,
204
- }))
205
- .flatMap((struct) => generateStruct(struct, { accessControl, level: 1 }))),
206
- ...schemaViews.flatMap((view) => generateStruct(pgTypeToSwiftStruct(view, columnsByTableId[view.id], "Select", {
207
- types,
208
- views,
209
- tables,
210
- }), { accessControl, level: 1 })),
211
- ...schemaCompositeTypes.flatMap((type) => generateStruct(pgCompositeTypeToSwiftStruct(type, { types, views, tables }), {
212
- accessControl,
213
- level: 1,
214
- })),
215
- "}",
216
- ];
217
- }),
218
- ];
219
- return `${output.join("\n")}\n`;
220
- };
221
- // TODO: Make this more robust. Currently doesn't handle range types - returns them as string.
222
- const pgTypeToSwiftType = (pgType, nullable, { types, views, tables, }) => {
223
- let swiftType;
224
- if (pgType === "bool") {
225
- swiftType = "Bool";
226
- }
227
- else if (pgType === "int2") {
228
- swiftType = "Int16";
229
- }
230
- else if (pgType === "int4") {
231
- swiftType = "Int32";
232
- }
233
- else if (pgType === "int8") {
234
- swiftType = "Int64";
235
- }
236
- else if (pgType === "float4") {
237
- swiftType = "Float";
238
- }
239
- else if (pgType === "float8") {
240
- swiftType = "Double";
241
- }
242
- else if (["numeric", "decimal"].includes(pgType)) {
243
- swiftType = "Decimal";
244
- }
245
- else if (pgType === "uuid") {
246
- swiftType = "UUID";
247
- }
248
- else if ([
249
- "bytea",
250
- "bpchar",
251
- "varchar",
252
- "date",
253
- "text",
254
- "citext",
255
- "time",
256
- "timetz",
257
- "timestamp",
258
- "timestamptz",
259
- "interval",
260
- "vector",
261
- ].includes(pgType)) {
262
- swiftType = "String";
263
- }
264
- else if (["json", "jsonb"].includes(pgType)) {
265
- swiftType = "AnyJSON";
266
- // `void` and `record` are pseudo-types: Postgres rejects them as a column or
267
- // composite attribute type ("column has pseudo-type void"), and this
268
- // generator only renders columns and attributes, never functions. So these
269
- // two arms are unreachable today and deliberately untested. They are kept
270
- // because they are the mappings to use if Swift ever emits functions, which
271
- // is where these types do appear.
272
- }
273
- else if (pgType === "void") {
274
- swiftType = "Void";
275
- }
276
- else if (pgType === "record") {
277
- swiftType = "JSONObject";
278
- }
279
- else if (pgType.startsWith("_")) {
280
- swiftType = `[${pgTypeToSwiftType(pgType.substring(1), false, { types, views, tables })}]`;
281
- }
282
- else {
283
- const enumType = types.find((type) => type.name === pgType && type.enums.length > 0);
284
- const compositeTypes = [...types, ...views, ...tables].find((type) => type.name === pgType);
285
- if (enumType) {
286
- swiftType = `${formatForSwiftTypeName(enumType.name)}`;
287
- }
288
- else if (compositeTypes) {
289
- // Append a `Select` to the composite type, as that is how is named in the generated struct.
290
- swiftType = `${formatForSwiftTypeName(compositeTypes.name)}Select`;
291
- }
292
- else {
293
- swiftType = "AnyJSON";
294
- }
295
- }
296
- return `${swiftType}${nullable ? "?" : ""}`;
297
- };
298
- function ident(level, options = { width: 2 }) {
299
- return " ".repeat(level * options.width);
300
- }
301
- /**
302
- * Converts a Postgres name to PascalCase.
303
- *
304
- * @example
305
- * ```ts
306
- * formatForSwiftTypeName('pokedex') // Pokedex
307
- * formatForSwiftTypeName('pokemon_center') // PokemonCenter
308
- * formatForSwiftTypeName('victory-road') // VictoryRoad
309
- * formatForSwiftTypeName('pokemon league') // PokemonLeague
310
- * formatForSwiftTypeName('_key_id_context') // _KeyIdContext
311
- * ```
312
- */
313
- function formatForSwiftTypeName(name) {
314
- // Preserve the initial underscore if it exists
315
- let prefix = "";
316
- if (name.startsWith("_")) {
317
- prefix = "_";
318
- name = name.slice(1); // Remove the initial underscore for processing
319
- }
320
- return (prefix +
321
- name
322
- .split(/[^a-zA-Z0-9]+/)
323
- .map((word) => {
324
- if (word) {
325
- return `${word.charAt(0).toUpperCase()}${word.slice(1)}`;
326
- }
327
- else {
328
- return "";
329
- }
330
- })
331
- .join(""));
332
- }
333
- const SWIFT_KEYWORDS = ["in", "default", "case"];
334
- /**
335
- * Converts a Postgres name to pascalCase.
336
- *
337
- * @example
338
- * ```ts
339
- * formatForSwiftTypeName('pokedex') // pokedex
340
- * formatForSwiftTypeName('pokemon_center') // pokemonCenter
341
- * formatForSwiftTypeName('victory-road') // victoryRoad
342
- * formatForSwiftTypeName('pokemon league') // pokemonLeague
343
- * ```
344
- */
345
- function formatForSwiftPropertyName(name) {
346
- const propertyName = name
347
- .split(/[^a-zA-Z0-9]/)
348
- .map((word, index) => {
349
- const lowerWord = word.toLowerCase();
350
- return index !== 0
351
- ? lowerWord.charAt(0).toUpperCase() + lowerWord.slice(1)
352
- : lowerWord;
353
- })
354
- .join("");
355
- return SWIFT_KEYWORDS.includes(propertyName)
356
- ? `\`${propertyName}\``
357
- : propertyName;
358
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Type-generation constants ported verbatim from
3
- * `postgres-meta/src/server/constants.ts`. Only the typegen-relevant values
4
- * live here; server/runtime constants stay in postgres-meta.
5
- *
6
- * Consumed by the TypeScript generator's function-argument handling.
7
- */
8
-
9
- // json/jsonb/text types
10
- export const VALID_UNNAMED_FUNCTION_ARG_TYPES = new Set([114, 3802, 25]);
11
- export const VALID_FUNCTION_ARGS_MODE = new Set(["in", "inout", "variadic"]);