From 57cae247410522a56def0551026893a8ecaecb1d Mon Sep 17 00:00:00 2001
From: Isaac Harris-Holt <isaac@withpluto.com>
Date: Thu, 21 Mar 2024 10:07:53 +0000
Subject: [PATCH 1/2] fix(typegen): deprecate  type in favour of

---
 src/server/templates/typescript.ts | 547 +++++++++++++++--------------
 test/server/typegen.ts             |  42 +--
 2 files changed, 295 insertions(+), 294 deletions(-)

diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts
index 563a2ce4..2fd1b54b 100644
--- a/src/server/templates/typescript.ts
+++ b/src/server/templates/typescript.ts
@@ -31,357 +31,368 @@ export const apply = async ({
     .forEach((c) => columnsByTableId[c.table_id].push(c))
 
   let output = `
-export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
+/** @deprecated Use \`unknown\` instead. \`Json\` will be removed in a future release. */
+export type Json = unknown
 
 export type Database = {
   ${schemas
-    .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-    .map((schema) => {
-      const schemaTables = tables
-        .filter((table) => table.schema === schema.name)
-        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      const schemaViews = [...views, ...materializedViews]
-        .filter((view) => view.schema === schema.name)
-        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      const schemaFunctions = functions
-        .filter((func) => {
-          if (func.schema !== schema.name) {
-            return false
-          }
+      .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      .map((schema) => {
+        const schemaTables = tables
+          .filter((table) => table.schema === schema.name)
+          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+        const schemaViews = [...views, ...materializedViews]
+          .filter((view) => view.schema === schema.name)
+          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+        const schemaFunctions = functions
+          .filter((func) => {
+            if (func.schema !== schema.name) {
+              return false
+            }
 
-          // Either:
-          // 1. All input args are be named, or
-          // 2. There is only one input arg which is unnamed
-          const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))
+            // Either:
+            // 1. All input args are be named, or
+            // 2. There is only one input arg which is unnamed
+            const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))
 
-          if (!inArgs.some(({ name }) => name === '')) {
-            return true
-          }
+            if (!inArgs.some(({ name }) => name === '')) {
+              return true
+            }
 
-          if (inArgs.length === 1) {
-            return true
-          }
+            if (inArgs.length === 1) {
+              return true
+            }
 
-          return false
-        })
-        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      const schemaEnums = types
-        .filter((type) => type.schema === schema.name && type.enums.length > 0)
-        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      const schemaCompositeTypes = types
-        .filter((type) => type.schema === schema.name && type.attributes.length > 0)
-        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      return `${JSON.stringify(schema.name)}: {
+            return false
+          })
+          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+        const schemaEnums = types
+          .filter((type) => type.schema === schema.name && type.enums.length > 0)
+          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+        const schemaCompositeTypes = types
+          .filter((type) => type.schema === schema.name && type.attributes.length > 0)
+          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+        return `${JSON.stringify(schema.name)}: {
           Tables: {
-            ${
-              schemaTables.length === 0
-                ? '[_ in never]: never'
-                : schemaTables.map(
-                    (table) => `${JSON.stringify(table.name)}: {
+            ${schemaTables.length === 0
+            ? '[_ in never]: never'
+            : schemaTables.map(
+              (table) => `${JSON.stringify(table.name)}: {
                   Row: {
                     ${[
-                      ...columnsByTableId[table.id].map(
-                        (column) =>
-                          `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                            types,
-                            schemas,
-                            tables,
-                            views,
-                          })} ${column.is_nullable ? '| null' : ''}`
-                      ),
-                      ...schemaFunctions
-                        .filter((fn) => fn.argument_types === table.name)
-                        .map((fn) => {
-                          const type = types.find(({ id }) => id === fn.return_type_id)
-                          let tsType = 'unknown'
-                          if (type) {
-                            tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                          }
-                          return `${JSON.stringify(fn.name)}: ${tsType} | null`
-                        }),
-                    ]}
+                  ...columnsByTableId[table.id].map(
+                    (column) =>
+                      `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                        types,
+                        schemas,
+                        tables,
+                        views,
+                      })} ${column.is_nullable ? '| null' : ''}`
+                  ),
+                  ...schemaFunctions
+                    .filter((fn) => fn.argument_types === table.name)
+                    .map((fn) => {
+                      const type = types.find(({ id }) => id === fn.return_type_id)
+                      let tsType = 'unknown'
+                      if (type) {
+                        tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                      }
+                      return `${JSON.stringify(fn.name)}: ${tsType} | null`
+                    }),
+                ]}
                   }
                   Insert: {
                     ${columnsByTableId[table.id].map((column) => {
-                      let output = JSON.stringify(column.name)
+                  let output = JSON.stringify(column.name)
 
-                      if (column.identity_generation === 'ALWAYS') {
-                        return `${output}?: never`
-                      }
+                  if (column.identity_generation === 'ALWAYS') {
+                    return `${output}?: never`
+                  }
 
-                      if (
-                        column.is_nullable ||
-                        column.is_identity ||
-                        column.default_value !== null
-                      ) {
-                        output += '?:'
-                      } else {
-                        output += ':'
-                      }
+                  if (
+                    column.is_nullable ||
+                    column.is_identity ||
+                    column.default_value !== null
+                  ) {
+                    output += '?:'
+                  } else {
+                    output += ':'
+                  }
 
-                      output += pgTypeToTsType(column.format, { types, schemas, tables, views })
+                  output += pgTypeToTsType(column.format, { types, schemas, tables, views })
 
-                      if (column.is_nullable) {
-                        output += '| null'
-                      }
+                  if (column.is_nullable) {
+                    output += '| null'
+                  }
 
-                      return output
-                    })}
+                  return output
+                })}
                   }
                   Update: {
                     ${columnsByTableId[table.id].map((column) => {
-                      let output = JSON.stringify(column.name)
+                  let output = JSON.stringify(column.name)
 
-                      if (column.identity_generation === 'ALWAYS') {
-                        return `${output}?: never`
-                      }
+                  if (column.identity_generation === 'ALWAYS') {
+                    return `${output}?: never`
+                  }
 
-                      output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })}`
+                  output += `?: ${pgTypeToTsType(column.format, {
+                    types,
+                    schemas,
+                    tables,
+                    views,
+                  })}`
 
-                      if (column.is_nullable) {
-                        output += '| null'
-                      }
+                  if (column.is_nullable) {
+                    output += '| null'
+                  }
 
-                      return output
-                    })}
+                  return output
+                })}
                   }
                   Relationships: [
                     ${relationships
-                      .filter(
-                        (relationship) =>
-                          relationship.schema === table.schema &&
-                          relationship.relation === table.name
-                      )
-                      .sort(
-                        (a, b) =>
-                          a.foreign_key_name.localeCompare(b.foreign_key_name) ||
-                          a.referenced_relation.localeCompare(b.referenced_relation)
-                      )
-                      .map(
-                        (relationship) => `{
+                  .filter(
+                    (relationship) =>
+                      relationship.schema === table.schema &&
+                      relationship.relation === table.name
+                  )
+                  .sort(
+                    (a, b) =>
+                      a.foreign_key_name.localeCompare(b.foreign_key_name) ||
+                      a.referenced_relation.localeCompare(b.referenced_relation)
+                  )
+                  .map(
+                    (relationship) => `{
                         foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
                         columns: ${JSON.stringify(relationship.columns)}
-                        ${
-                          detectOneToOneRelationships
-                            ? `isOneToOne: ${relationship.is_one_to_one};`
-                            : ''
-                        }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
+                        ${detectOneToOneRelationships
+                        ? `isOneToOne: ${relationship.is_one_to_one};`
+                        : ''
+                      }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
                         referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
                       }`
-                      )}
+                  )}
                   ]
                 }`
-                  )
-            }
+            )
+          }
           }
           Views: {
-            ${
-              schemaViews.length === 0
-                ? '[_ in never]: never'
-                : schemaViews.map(
-                    (view) => `${JSON.stringify(view.name)}: {
+            ${schemaViews.length === 0
+            ? '[_ in never]: never'
+            : schemaViews.map(
+              (view) => `${JSON.stringify(view.name)}: {
                   Row: {
                     ${columnsByTableId[view.id].map(
-                      (column) =>
-                        `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                          types,
-                          schemas,
-                          tables,
-                          views,
-                        })} ${column.is_nullable ? '| null' : ''}`
-                    )}
+                (column) =>
+                  `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                    types,
+                    schemas,
+                    tables,
+                    views,
+                  })} ${column.is_nullable ? '| null' : ''}`
+              )}
                   }
-                  ${
-                    'is_updatable' in view && view.is_updatable
-                      ? `Insert: {
+                  ${'is_updatable' in view && view.is_updatable
+                  ? `Insert: {
                            ${columnsByTableId[view.id].map((column) => {
-                             let output = JSON.stringify(column.name)
+                    let output = JSON.stringify(column.name)
 
-                             if (!column.is_updatable) {
-                               return `${output}?: never`
-                             }
+                    if (!column.is_updatable) {
+                      return `${output}?: never`
+                    }
 
-                             output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })} | null`
+                    output += `?: ${pgTypeToTsType(column.format, {
+                      types,
+                      schemas,
+                      tables,
+                      views,
+                    })} | null`
 
-                             return output
-                           })}
+                    return output
+                  })}
                          }
                          Update: {
                            ${columnsByTableId[view.id].map((column) => {
-                             let output = JSON.stringify(column.name)
+                    let output = JSON.stringify(column.name)
 
-                             if (!column.is_updatable) {
-                               return `${output}?: never`
-                             }
+                    if (!column.is_updatable) {
+                      return `${output}?: never`
+                    }
 
-                             output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })} | null`
+                    output += `?: ${pgTypeToTsType(column.format, {
+                      types,
+                      schemas,
+                      tables,
+                      views,
+                    })} | null`
 
-                             return output
-                           })}
+                    return output
+                  })}
                          }
                         `
-                      : ''
-                  }Relationships: [
+                  : ''
+                }Relationships: [
                     ${relationships
-                      .filter(
-                        (relationship) =>
-                          relationship.schema === view.schema && relationship.relation === view.name
-                      )
-                      .sort(({ foreign_key_name: a }, { foreign_key_name: b }) =>
-                        a.localeCompare(b)
-                      )
-                      .map(
-                        (relationship) => `{
+                  .filter(
+                    (relationship) =>
+                      relationship.schema === view.schema && relationship.relation === view.name
+                  )
+                  .sort(({ foreign_key_name: a }, { foreign_key_name: b }) =>
+                    a.localeCompare(b)
+                  )
+                  .map(
+                    (relationship) => `{
                         foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
                         columns: ${JSON.stringify(relationship.columns)}
-                        ${
-                          detectOneToOneRelationships
-                            ? `isOneToOne: ${relationship.is_one_to_one};`
-                            : ''
-                        }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
+                        ${detectOneToOneRelationships
+                        ? `isOneToOne: ${relationship.is_one_to_one};`
+                        : ''
+                      }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
                         referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
                       }`
-                      )}
+                  )}
                   ]
                 }`
-                  )
-            }
+            )
+          }
           }
           Functions: {
             ${(() => {
-              if (schemaFunctions.length === 0) {
-                return '[_ in never]: never'
-              }
-
-              const schemaFunctionsGroupedByName = schemaFunctions.reduce(
-                (acc, curr) => {
-                  acc[curr.name] ??= []
-                  acc[curr.name].push(curr)
-                  return acc
-                },
-                {} as Record<string, PostgresFunction[]>
-              )
-
-              return Object.entries(schemaFunctionsGroupedByName).map(
-                ([fnName, fns]) =>
-                  `${JSON.stringify(fnName)}: ${fns
-                    .map(
-                      ({
-                        args,
-                        return_type_id,
-                        return_type_relation_id,
-                        is_set_returning_function,
-                      }) => `{
+            if (schemaFunctions.length === 0) {
+              return '[_ in never]: never'
+            }
+
+            const schemaFunctionsGroupedByName = schemaFunctions.reduce((acc, curr) => {
+              acc[curr.name] ??= []
+              acc[curr.name].push(curr)
+              return acc
+            }, {} as Record<string, PostgresFunction[]>)
+
+            return Object.entries(schemaFunctionsGroupedByName).map(
+              ([fnName, fns]) =>
+                `${JSON.stringify(fnName)}: ${fns
+                  .map(
+                    ({
+                      args,
+                      return_type_id,
+                      return_type_relation_id,
+                      is_set_returning_function,
+                    }) => `{
                   Args: ${(() => {
-                    const inArgs = args.filter(({ mode }) => mode === 'in')
+                        const inArgs = args.filter(({ mode }) => mode === 'in')
 
-                    if (inArgs.length === 0) {
-                      return 'Record<PropertyKey, never>'
-                    }
+                        if (inArgs.length === 0) {
+                          return 'Record<PropertyKey, never>'
+                        }
 
-                    const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
-                      const type = types.find(({ id }) => id === type_id)
-                      let tsType = 'unknown'
-                      if (type) {
-                        tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                      }
-                      return { name, type: tsType, has_default }
-                    })
+                        const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
+                          const type = types.find(({ id }) => id === type_id)
+                          let tsType = 'unknown'
+                          if (type) {
+                            tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                          }
+                          return { name, type: tsType, has_default }
+                        })
 
-                    return `{
+                        return `{
                       ${argsNameAndType.map(
-                        ({ name, type, has_default }) =>
-                          `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
-                      )}
+                          ({ name, type, has_default }) =>
+                            `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
+                        )}
                     }`
-                  })()}
+                      })()}
                   Returns: (${(() => {
-                    // Case 1: `returns table`.
-                    const tableArgs = args.filter(({ mode }) => mode === 'table')
-                    if (tableArgs.length > 0) {
-                      const argsNameAndType = tableArgs.map(({ name, type_id }) => {
-                        const type = types.find(({ id }) => id === type_id)
-                        let tsType = 'unknown'
-                        if (type) {
-                          tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                        }
-                        return { name, type: tsType }
-                      })
-
-                      return `{
+                        // Case 1: `returns table`.
+                        const tableArgs = args.filter(({ mode }) => mode === 'table')
+                        if (tableArgs.length > 0) {
+                          const argsNameAndType = tableArgs.map(({ name, type_id }) => {
+                            const type = types.find(({ id }) => id === type_id)
+                            let tsType = 'unknown'
+                            if (type) {
+                              tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                            }
+                            return { name, type: tsType }
+                          })
+
+                          return `{
                         ${argsNameAndType.map(
-                          ({ name, type }) => `${JSON.stringify(name)}: ${type}`
-                        )}
+                            ({ name, type }) => `${JSON.stringify(name)}: ${type}`
+                          )}
                       }`
-                    }
+                        }
 
-                    // Case 2: returns a relation's row type.
-                    const relation = [...tables, ...views].find(
-                      ({ id }) => id === return_type_relation_id
-                    )
-                    if (relation) {
-                      return `{
+                        // Case 2: returns a relation's row type.
+                        const relation = [...tables, ...views].find(
+                          ({ id }) => id === return_type_relation_id
+                        )
+                        if (relation) {
+                          return `{
                         ${columnsByTableId[relation.id].map(
-                          (column) =>
-                            `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                              types,
-                              schemas,
-                              tables,
-                              views,
-                            })} ${column.is_nullable ? '| null' : ''}`
-                        )}
+                            (column) =>
+                              `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                                types,
+                                schemas,
+                                tables,
+                                views,
+                              })} ${column.is_nullable ? '| null' : ''}`
+                          )}
                       }`
-                    }
+                        }
 
-                    // Case 3: returns base/array/composite/enum type.
-                    const type = types.find(({ id }) => id === return_type_id)
-                    if (type) {
-                      return pgTypeToTsType(type.name, { types, schemas, tables, views })
-                    }
+                        // Case 3: returns base/array/composite/enum type.
+                        const type = types.find(({ id }) => id === return_type_id)
+                        if (type) {
+                          return pgTypeToTsType(type.name, { types, schemas, tables, views })
+                        }
 
-                    return 'unknown'
-                  })()})${is_set_returning_function ? '[]' : ''}
+                        return 'unknown'
+                      })()})${is_set_returning_function ? '[]' : ''}
                 }`
-                    )
-                    // We only sorted by name on schemaFunctions - here we sort by arg names, arg types, and return type.
-                    .sort()
-                    .join('|')}`
-              )
-            })()}
+                  )
+                  // We only sorted by name on schemaFunctions - here we sort by arg names, arg types, and return type.
+                  .sort()
+                  .join('|')}`
+            )
+          })()}
           }
           Enums: {
-            ${
-              schemaEnums.length === 0
-                ? '[_ in never]: never'
-                : schemaEnums.map(
-                    (enum_) =>
-                      `${JSON.stringify(enum_.name)}: ${enum_.enums
-                        .map((variant) => JSON.stringify(variant))
-                        .join('|')}`
-                  )
-            }
+            ${schemaEnums.length === 0
+            ? '[_ in never]: never'
+            : schemaEnums.map(
+              (enum_) =>
+                `${JSON.stringify(enum_.name)}: ${enum_.enums
+                  .map((variant) => JSON.stringify(variant))
+                  .join('|')}`
+            )
+          }
           }
           CompositeTypes: {
-            ${
-              schemaCompositeTypes.length === 0
-                ? '[_ in never]: never'
-                : schemaCompositeTypes.map(
-                    ({ name, attributes }) =>
-                      `${JSON.stringify(name)}: {
+            ${schemaCompositeTypes.length === 0
+            ? '[_ in never]: never'
+            : schemaCompositeTypes.map(
+              ({ name, attributes }) =>
+                `${JSON.stringify(name)}: {
                         ${attributes.map(({ name, type_id }) => {
-                          const type = types.find(({ id }) => id === type_id)
-                          let tsType = 'unknown'
-                          if (type) {
-                            tsType = `${pgTypeToTsType(type.name, { types, schemas, tables, views })} | null`
-                          }
-                          return `${JSON.stringify(name)}: ${tsType}`
-                        })}
+                  const type = types.find(({ id }) => id === type_id)
+                  let tsType = 'unknown'
+                  if (type) {
+                    tsType = `${pgTypeToTsType(type.name, {
+                      types,
+                      schemas,
+                      tables,
+                      views,
+                    })} | null`
+                  }
+                  return `${JSON.stringify(name)}: ${tsType}`
+                })}
                       }`
-                  )
-            }
+            )
+          }
           }
         }`
-    })}
+      })}
 }
 
 type PublicSchema = Database[Extract<keyof Database, "public">]
@@ -509,7 +520,7 @@ const pgTypeToTsType = (
   ) {
     return 'string'
   } else if (['json', 'jsonb'].includes(pgType)) {
-    return 'Json'
+    return 'unknown'
   } else if (pgType === 'void') {
     return 'undefined'
   } else if (pgType === 'record') {
diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index ddf31801..2db304d6 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -4,13 +4,8 @@ import { app } from './utils'
 test('typegen', async () => {
   const { body } = await app.inject({ method: 'GET', path: '/generators/typescript' })
   expect(body).toMatchInlineSnapshot(`
-    "export type Json =
-      | string
-      | number
-      | boolean
-      | null
-      | { [key: string]: Json | undefined }
-      | Json[]
+    "/** @deprecated Use \`unknown\` instead. \`Json\` will be removed in a future release. */
+    export type Json = unknown
 
     export type Database = {
       public: {
@@ -41,7 +36,7 @@ test('typegen', async () => {
               category: number | null
               created_at: string
               id: number
-              metadata: Json | null
+              metadata: unknown | null
               name: string
               status: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -49,7 +44,7 @@ test('typegen', async () => {
               category?: number | null
               created_at: string
               id?: number
-              metadata?: Json | null
+              metadata?: unknown | null
               name: string
               status?: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -57,7 +52,7 @@ test('typegen', async () => {
               category?: number | null
               created_at?: string
               id?: number
-              metadata?: Json | null
+              metadata?: unknown | null
               name?: string
               status?: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -183,19 +178,19 @@ test('typegen', async () => {
             Row: {
               created_at: string | null
               id: number
-              previous_value: Json | null
+              previous_value: unknown | null
               user_id: number | null
             }
             Insert: {
               created_at?: string | null
               id?: number
-              previous_value?: Json | null
+              previous_value?: unknown | null
               user_id?: number | null
             }
             Update: {
               created_at?: string | null
               id?: number
-              previous_value?: Json | null
+              previous_value?: unknown | null
               user_id?: number | null
             }
             Relationships: []
@@ -487,13 +482,8 @@ test('typegen w/ one-to-one relationships', async () => {
     query: { detect_one_to_one_relationships: 'true' },
   })
   expect(body).toMatchInlineSnapshot(`
-    "export type Json =
-      | string
-      | number
-      | boolean
-      | null
-      | { [key: string]: Json | undefined }
-      | Json[]
+    "/** @deprecated Use \`unknown\` instead. \`Json\` will be removed in a future release. */
+    export type Json = unknown
 
     export type Database = {
       public: {
@@ -524,7 +514,7 @@ test('typegen w/ one-to-one relationships', async () => {
               category: number | null
               created_at: string
               id: number
-              metadata: Json | null
+              metadata: unknown | null
               name: string
               status: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -532,7 +522,7 @@ test('typegen w/ one-to-one relationships', async () => {
               category?: number | null
               created_at: string
               id?: number
-              metadata?: Json | null
+              metadata?: unknown | null
               name: string
               status?: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -540,7 +530,7 @@ test('typegen w/ one-to-one relationships', async () => {
               category?: number | null
               created_at?: string
               id?: number
-              metadata?: Json | null
+              metadata?: unknown | null
               name?: string
               status?: Database["public"]["Enums"]["meme_status"] | null
             }
@@ -673,19 +663,19 @@ test('typegen w/ one-to-one relationships', async () => {
             Row: {
               created_at: string | null
               id: number
-              previous_value: Json | null
+              previous_value: unknown | null
               user_id: number | null
             }
             Insert: {
               created_at?: string | null
               id?: number
-              previous_value?: Json | null
+              previous_value?: unknown | null
               user_id?: number | null
             }
             Update: {
               created_at?: string | null
               id?: number
-              previous_value?: Json | null
+              previous_value?: unknown | null
               user_id?: number | null
             }
             Relationships: []

From 35488c6e7daa39c1bda3445e330c5bc047739454 Mon Sep 17 00:00:00 2001
From: Isaac Harris-Holt <isaac@withpluto.com>
Date: Thu, 21 Mar 2024 10:10:09 +0000
Subject: [PATCH 2/2] fix(typegen): format

---
 src/server/templates/typescript.ts | 562 +++++++++++++++--------------
 1 file changed, 286 insertions(+), 276 deletions(-)

diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts
index 2fd1b54b..1d5fa925 100644
--- a/src/server/templates/typescript.ts
+++ b/src/server/templates/typescript.ts
@@ -36,363 +36,373 @@ export type Json = unknown
 
 export type Database = {
   ${schemas
-      .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-      .map((schema) => {
-        const schemaTables = tables
-          .filter((table) => table.schema === schema.name)
-          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-        const schemaViews = [...views, ...materializedViews]
-          .filter((view) => view.schema === schema.name)
-          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-        const schemaFunctions = functions
-          .filter((func) => {
-            if (func.schema !== schema.name) {
-              return false
-            }
+    .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+    .map((schema) => {
+      const schemaTables = tables
+        .filter((table) => table.schema === schema.name)
+        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      const schemaViews = [...views, ...materializedViews]
+        .filter((view) => view.schema === schema.name)
+        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      const schemaFunctions = functions
+        .filter((func) => {
+          if (func.schema !== schema.name) {
+            return false
+          }
 
-            // Either:
-            // 1. All input args are be named, or
-            // 2. There is only one input arg which is unnamed
-            const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))
+          // Either:
+          // 1. All input args are be named, or
+          // 2. There is only one input arg which is unnamed
+          const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))
 
-            if (!inArgs.some(({ name }) => name === '')) {
-              return true
-            }
+          if (!inArgs.some(({ name }) => name === '')) {
+            return true
+          }
 
-            if (inArgs.length === 1) {
-              return true
-            }
+          if (inArgs.length === 1) {
+            return true
+          }
 
-            return false
-          })
-          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-        const schemaEnums = types
-          .filter((type) => type.schema === schema.name && type.enums.length > 0)
-          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-        const schemaCompositeTypes = types
-          .filter((type) => type.schema === schema.name && type.attributes.length > 0)
-          .sort(({ name: a }, { name: b }) => a.localeCompare(b))
-        return `${JSON.stringify(schema.name)}: {
+          return false
+        })
+        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      const schemaEnums = types
+        .filter((type) => type.schema === schema.name && type.enums.length > 0)
+        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      const schemaCompositeTypes = types
+        .filter((type) => type.schema === schema.name && type.attributes.length > 0)
+        .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+      return `${JSON.stringify(schema.name)}: {
           Tables: {
-            ${schemaTables.length === 0
-            ? '[_ in never]: never'
-            : schemaTables.map(
-              (table) => `${JSON.stringify(table.name)}: {
+            ${
+              schemaTables.length === 0
+                ? '[_ in never]: never'
+                : schemaTables.map(
+                    (table) => `${JSON.stringify(table.name)}: {
                   Row: {
                     ${[
-                  ...columnsByTableId[table.id].map(
-                    (column) =>
-                      `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                        types,
-                        schemas,
-                        tables,
-                        views,
-                      })} ${column.is_nullable ? '| null' : ''}`
-                  ),
-                  ...schemaFunctions
-                    .filter((fn) => fn.argument_types === table.name)
-                    .map((fn) => {
-                      const type = types.find(({ id }) => id === fn.return_type_id)
-                      let tsType = 'unknown'
-                      if (type) {
-                        tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                      }
-                      return `${JSON.stringify(fn.name)}: ${tsType} | null`
-                    }),
-                ]}
+                      ...columnsByTableId[table.id].map(
+                        (column) =>
+                          `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                            types,
+                            schemas,
+                            tables,
+                            views,
+                          })} ${column.is_nullable ? '| null' : ''}`
+                      ),
+                      ...schemaFunctions
+                        .filter((fn) => fn.argument_types === table.name)
+                        .map((fn) => {
+                          const type = types.find(({ id }) => id === fn.return_type_id)
+                          let tsType = 'unknown'
+                          if (type) {
+                            tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                          }
+                          return `${JSON.stringify(fn.name)}: ${tsType} | null`
+                        }),
+                    ]}
                   }
                   Insert: {
                     ${columnsByTableId[table.id].map((column) => {
-                  let output = JSON.stringify(column.name)
+                      let output = JSON.stringify(column.name)
 
-                  if (column.identity_generation === 'ALWAYS') {
-                    return `${output}?: never`
-                  }
+                      if (column.identity_generation === 'ALWAYS') {
+                        return `${output}?: never`
+                      }
 
-                  if (
-                    column.is_nullable ||
-                    column.is_identity ||
-                    column.default_value !== null
-                  ) {
-                    output += '?:'
-                  } else {
-                    output += ':'
-                  }
+                      if (
+                        column.is_nullable ||
+                        column.is_identity ||
+                        column.default_value !== null
+                      ) {
+                        output += '?:'
+                      } else {
+                        output += ':'
+                      }
 
-                  output += pgTypeToTsType(column.format, { types, schemas, tables, views })
+                      output += pgTypeToTsType(column.format, { types, schemas, tables, views })
 
-                  if (column.is_nullable) {
-                    output += '| null'
-                  }
+                      if (column.is_nullable) {
+                        output += '| null'
+                      }
 
-                  return output
-                })}
+                      return output
+                    })}
                   }
                   Update: {
                     ${columnsByTableId[table.id].map((column) => {
-                  let output = JSON.stringify(column.name)
+                      let output = JSON.stringify(column.name)
 
-                  if (column.identity_generation === 'ALWAYS') {
-                    return `${output}?: never`
-                  }
+                      if (column.identity_generation === 'ALWAYS') {
+                        return `${output}?: never`
+                      }
 
-                  output += `?: ${pgTypeToTsType(column.format, {
-                    types,
-                    schemas,
-                    tables,
-                    views,
-                  })}`
+                      output += `?: ${pgTypeToTsType(column.format, {
+                        types,
+                        schemas,
+                        tables,
+                        views,
+                      })}`
 
-                  if (column.is_nullable) {
-                    output += '| null'
-                  }
+                      if (column.is_nullable) {
+                        output += '| null'
+                      }
 
-                  return output
-                })}
+                      return output
+                    })}
                   }
                   Relationships: [
                     ${relationships
-                  .filter(
-                    (relationship) =>
-                      relationship.schema === table.schema &&
-                      relationship.relation === table.name
-                  )
-                  .sort(
-                    (a, b) =>
-                      a.foreign_key_name.localeCompare(b.foreign_key_name) ||
-                      a.referenced_relation.localeCompare(b.referenced_relation)
-                  )
-                  .map(
-                    (relationship) => `{
+                      .filter(
+                        (relationship) =>
+                          relationship.schema === table.schema &&
+                          relationship.relation === table.name
+                      )
+                      .sort(
+                        (a, b) =>
+                          a.foreign_key_name.localeCompare(b.foreign_key_name) ||
+                          a.referenced_relation.localeCompare(b.referenced_relation)
+                      )
+                      .map(
+                        (relationship) => `{
                         foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
                         columns: ${JSON.stringify(relationship.columns)}
-                        ${detectOneToOneRelationships
-                        ? `isOneToOne: ${relationship.is_one_to_one};`
-                        : ''
-                      }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
+                        ${
+                          detectOneToOneRelationships
+                            ? `isOneToOne: ${relationship.is_one_to_one};`
+                            : ''
+                        }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
                         referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
                       }`
-                  )}
+                      )}
                   ]
                 }`
-            )
-          }
+                  )
+            }
           }
           Views: {
-            ${schemaViews.length === 0
-            ? '[_ in never]: never'
-            : schemaViews.map(
-              (view) => `${JSON.stringify(view.name)}: {
+            ${
+              schemaViews.length === 0
+                ? '[_ in never]: never'
+                : schemaViews.map(
+                    (view) => `${JSON.stringify(view.name)}: {
                   Row: {
                     ${columnsByTableId[view.id].map(
-                (column) =>
-                  `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                    types,
-                    schemas,
-                    tables,
-                    views,
-                  })} ${column.is_nullable ? '| null' : ''}`
-              )}
+                      (column) =>
+                        `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                          types,
+                          schemas,
+                          tables,
+                          views,
+                        })} ${column.is_nullable ? '| null' : ''}`
+                    )}
                   }
-                  ${'is_updatable' in view && view.is_updatable
-                  ? `Insert: {
+                  ${
+                    'is_updatable' in view && view.is_updatable
+                      ? `Insert: {
                            ${columnsByTableId[view.id].map((column) => {
-                    let output = JSON.stringify(column.name)
+                             let output = JSON.stringify(column.name)
 
-                    if (!column.is_updatable) {
-                      return `${output}?: never`
-                    }
+                             if (!column.is_updatable) {
+                               return `${output}?: never`
+                             }
 
-                    output += `?: ${pgTypeToTsType(column.format, {
-                      types,
-                      schemas,
-                      tables,
-                      views,
-                    })} | null`
+                             output += `?: ${pgTypeToTsType(column.format, {
+                               types,
+                               schemas,
+                               tables,
+                               views,
+                             })} | null`
 
-                    return output
-                  })}
+                             return output
+                           })}
                          }
                          Update: {
                            ${columnsByTableId[view.id].map((column) => {
-                    let output = JSON.stringify(column.name)
+                             let output = JSON.stringify(column.name)
 
-                    if (!column.is_updatable) {
-                      return `${output}?: never`
-                    }
+                             if (!column.is_updatable) {
+                               return `${output}?: never`
+                             }
 
-                    output += `?: ${pgTypeToTsType(column.format, {
-                      types,
-                      schemas,
-                      tables,
-                      views,
-                    })} | null`
+                             output += `?: ${pgTypeToTsType(column.format, {
+                               types,
+                               schemas,
+                               tables,
+                               views,
+                             })} | null`
 
-                    return output
-                  })}
+                             return output
+                           })}
                          }
                         `
-                  : ''
-                }Relationships: [
+                      : ''
+                  }Relationships: [
                     ${relationships
-                  .filter(
-                    (relationship) =>
-                      relationship.schema === view.schema && relationship.relation === view.name
-                  )
-                  .sort(({ foreign_key_name: a }, { foreign_key_name: b }) =>
-                    a.localeCompare(b)
-                  )
-                  .map(
-                    (relationship) => `{
+                      .filter(
+                        (relationship) =>
+                          relationship.schema === view.schema && relationship.relation === view.name
+                      )
+                      .sort(({ foreign_key_name: a }, { foreign_key_name: b }) =>
+                        a.localeCompare(b)
+                      )
+                      .map(
+                        (relationship) => `{
                         foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
                         columns: ${JSON.stringify(relationship.columns)}
-                        ${detectOneToOneRelationships
-                        ? `isOneToOne: ${relationship.is_one_to_one};`
-                        : ''
-                      }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
+                        ${
+                          detectOneToOneRelationships
+                            ? `isOneToOne: ${relationship.is_one_to_one};`
+                            : ''
+                        }referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
                         referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
                       }`
-                  )}
+                      )}
                   ]
                 }`
-            )
-          }
+                  )
+            }
           }
           Functions: {
             ${(() => {
-            if (schemaFunctions.length === 0) {
-              return '[_ in never]: never'
-            }
-
-            const schemaFunctionsGroupedByName = schemaFunctions.reduce((acc, curr) => {
-              acc[curr.name] ??= []
-              acc[curr.name].push(curr)
-              return acc
-            }, {} as Record<string, PostgresFunction[]>)
-
-            return Object.entries(schemaFunctionsGroupedByName).map(
-              ([fnName, fns]) =>
-                `${JSON.stringify(fnName)}: ${fns
-                  .map(
-                    ({
-                      args,
-                      return_type_id,
-                      return_type_relation_id,
-                      is_set_returning_function,
-                    }) => `{
+              if (schemaFunctions.length === 0) {
+                return '[_ in never]: never'
+              }
+
+              const schemaFunctionsGroupedByName = schemaFunctions.reduce(
+                (acc, curr) => {
+                  acc[curr.name] ??= []
+                  acc[curr.name].push(curr)
+                  return acc
+                },
+                {} as Record<string, PostgresFunction[]>
+              )
+
+              return Object.entries(schemaFunctionsGroupedByName).map(
+                ([fnName, fns]) =>
+                  `${JSON.stringify(fnName)}: ${fns
+                    .map(
+                      ({
+                        args,
+                        return_type_id,
+                        return_type_relation_id,
+                        is_set_returning_function,
+                      }) => `{
                   Args: ${(() => {
-                        const inArgs = args.filter(({ mode }) => mode === 'in')
+                    const inArgs = args.filter(({ mode }) => mode === 'in')
 
-                        if (inArgs.length === 0) {
-                          return 'Record<PropertyKey, never>'
-                        }
+                    if (inArgs.length === 0) {
+                      return 'Record<PropertyKey, never>'
+                    }
 
-                        const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
-                          const type = types.find(({ id }) => id === type_id)
-                          let tsType = 'unknown'
-                          if (type) {
-                            tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                          }
-                          return { name, type: tsType, has_default }
-                        })
+                    const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => {
+                      const type = types.find(({ id }) => id === type_id)
+                      let tsType = 'unknown'
+                      if (type) {
+                        tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                      }
+                      return { name, type: tsType, has_default }
+                    })
 
-                        return `{
+                    return `{
                       ${argsNameAndType.map(
-                          ({ name, type, has_default }) =>
-                            `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
-                        )}
+                        ({ name, type, has_default }) =>
+                          `${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
+                      )}
                     }`
-                      })()}
+                  })()}
                   Returns: (${(() => {
-                        // Case 1: `returns table`.
-                        const tableArgs = args.filter(({ mode }) => mode === 'table')
-                        if (tableArgs.length > 0) {
-                          const argsNameAndType = tableArgs.map(({ name, type_id }) => {
-                            const type = types.find(({ id }) => id === type_id)
-                            let tsType = 'unknown'
-                            if (type) {
-                              tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
-                            }
-                            return { name, type: tsType }
-                          })
-
-                          return `{
+                    // Case 1: `returns table`.
+                    const tableArgs = args.filter(({ mode }) => mode === 'table')
+                    if (tableArgs.length > 0) {
+                      const argsNameAndType = tableArgs.map(({ name, type_id }) => {
+                        const type = types.find(({ id }) => id === type_id)
+                        let tsType = 'unknown'
+                        if (type) {
+                          tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
+                        }
+                        return { name, type: tsType }
+                      })
+
+                      return `{
                         ${argsNameAndType.map(
-                            ({ name, type }) => `${JSON.stringify(name)}: ${type}`
-                          )}
+                          ({ name, type }) => `${JSON.stringify(name)}: ${type}`
+                        )}
                       }`
-                        }
+                    }
 
-                        // Case 2: returns a relation's row type.
-                        const relation = [...tables, ...views].find(
-                          ({ id }) => id === return_type_relation_id
-                        )
-                        if (relation) {
-                          return `{
+                    // Case 2: returns a relation's row type.
+                    const relation = [...tables, ...views].find(
+                      ({ id }) => id === return_type_relation_id
+                    )
+                    if (relation) {
+                      return `{
                         ${columnsByTableId[relation.id].map(
-                            (column) =>
-                              `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
-                                types,
-                                schemas,
-                                tables,
-                                views,
-                              })} ${column.is_nullable ? '| null' : ''}`
-                          )}
+                          (column) =>
+                            `${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
+                              types,
+                              schemas,
+                              tables,
+                              views,
+                            })} ${column.is_nullable ? '| null' : ''}`
+                        )}
                       }`
-                        }
+                    }
 
-                        // Case 3: returns base/array/composite/enum type.
-                        const type = types.find(({ id }) => id === return_type_id)
-                        if (type) {
-                          return pgTypeToTsType(type.name, { types, schemas, tables, views })
-                        }
+                    // Case 3: returns base/array/composite/enum type.
+                    const type = types.find(({ id }) => id === return_type_id)
+                    if (type) {
+                      return pgTypeToTsType(type.name, { types, schemas, tables, views })
+                    }
 
-                        return 'unknown'
-                      })()})${is_set_returning_function ? '[]' : ''}
+                    return 'unknown'
+                  })()})${is_set_returning_function ? '[]' : ''}
                 }`
-                  )
-                  // We only sorted by name on schemaFunctions - here we sort by arg names, arg types, and return type.
-                  .sort()
-                  .join('|')}`
-            )
-          })()}
+                    )
+                    // We only sorted by name on schemaFunctions - here we sort by arg names, arg types, and return type.
+                    .sort()
+                    .join('|')}`
+              )
+            })()}
           }
           Enums: {
-            ${schemaEnums.length === 0
-            ? '[_ in never]: never'
-            : schemaEnums.map(
-              (enum_) =>
-                `${JSON.stringify(enum_.name)}: ${enum_.enums
-                  .map((variant) => JSON.stringify(variant))
-                  .join('|')}`
-            )
-          }
+            ${
+              schemaEnums.length === 0
+                ? '[_ in never]: never'
+                : schemaEnums.map(
+                    (enum_) =>
+                      `${JSON.stringify(enum_.name)}: ${enum_.enums
+                        .map((variant) => JSON.stringify(variant))
+                        .join('|')}`
+                  )
+            }
           }
           CompositeTypes: {
-            ${schemaCompositeTypes.length === 0
-            ? '[_ in never]: never'
-            : schemaCompositeTypes.map(
-              ({ name, attributes }) =>
-                `${JSON.stringify(name)}: {
+            ${
+              schemaCompositeTypes.length === 0
+                ? '[_ in never]: never'
+                : schemaCompositeTypes.map(
+                    ({ name, attributes }) =>
+                      `${JSON.stringify(name)}: {
                         ${attributes.map(({ name, type_id }) => {
-                  const type = types.find(({ id }) => id === type_id)
-                  let tsType = 'unknown'
-                  if (type) {
-                    tsType = `${pgTypeToTsType(type.name, {
-                      types,
-                      schemas,
-                      tables,
-                      views,
-                    })} | null`
-                  }
-                  return `${JSON.stringify(name)}: ${tsType}`
-                })}
+                          const type = types.find(({ id }) => id === type_id)
+                          let tsType = 'unknown'
+                          if (type) {
+                            tsType = `${pgTypeToTsType(type.name, {
+                              types,
+                              schemas,
+                              tables,
+                              views,
+                            })} | null`
+                          }
+                          return `${JSON.stringify(name)}: ${tsType}`
+                        })}
                       }`
-            )
-          }
+                  )
+            }
           }
         }`
-      })}
+    })}
 }
 
 type PublicSchema = Database[Extract<keyof Database, "public">]