diff --git a/repro_output.txt b/repro_output.txt new file mode 100644 index 00000000..ddabc179 --- /dev/null +++ b/repro_output.txt @@ -0,0 +1,174 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + public: { + Tables: { + products: { + Row: { + id: number + name: string + fn_with_single_unnamed_table_arg: string | null + name_translated: string | null + } + Insert: { + id?: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + fn_with_single_unnamed_table_arg: { + Args: { "": Database["public"]["Tables"]["products"]["Row"] } + Returns: string + } + name_translated: { + Args: { "": Database["public"]["Tables"]["products"]["Row"] } + Returns: string + } + zero_arg_fn: { Args: Record; Returns: number } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 352c4ddc..82f8938a 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -193,11 +193,7 @@ export const apply = async ({ inArgs[0].name === '' && (VALID_UNNAMED_FUNCTION_ARG_TYPES.has(inArgs[0].type_id) || // OR if the function have a single unnamed args which is another table (embeded function) - (relationTypeByIds.get(inArgs[0].type_id) && - getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id)) || - // OR if the function takes a table row but doesn't qualify as embedded (for error reporting) - (relationTypeByIds.get(inArgs[0].type_id) && - !getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id)))) + relationTypeByIds.get(inArgs[0].type_id))) ) { introspectionBySchema[func.schema].functions.push({ fn: func, inArgs }) } @@ -300,24 +296,24 @@ export const apply = async ({ if (relation) { return `{ ${columnsByTableId[relation.id] - .map((column) => - generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: column.is_nullable, - is_optional: false, - }, - { - types, - schemas, - tables, - views, - } - ) - ) - .join(',\n')} + .map((column) => + generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: column.is_nullable, + is_optional: false, + }, + { + types, + schemas, + tables, + views, + } + ) + ) + .join(',\n')} }` } @@ -340,7 +336,8 @@ export const apply = async ({ inArgs.length === 1 && inArgs[0].name === '' && relationTypeByIds.get(inArgs[0].type_id) && - !getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id) + !getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id) && + !typesById.get(fn.return_type_id) ) { return true } @@ -410,7 +407,7 @@ export const apply = async ({ ) => { return fns .map(({ fn, inArgs }) => { - let argsType = 'never' + let argsType = 'Record' let returnType = getFunctionReturnType(schema, fn) // Check for specific error cases @@ -523,217 +520,212 @@ export type Database = { } = introspectionBySchema[schema.name] return `${JSON.stringify(schema.name)}: { Tables: { - ${ - schemaTables.length === 0 - ? '[_ in never]: never' - : schemaTables.map( - ({ table, relationships }) => `${JSON.stringify(table.name)}: { + ${schemaTables.length === 0 + ? '[_ in never]: never' + : schemaTables.map( + ({ table, relationships }) => `${JSON.stringify(table.name)}: { Row: { ${[ - ...columnsByTableId[table.id].map((column) => - generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: column.is_nullable, - is_optional: false, - }, - { types, schemas, tables, views } - ) - ), - ...schemaFunctions - .filter(({ fn }) => fn.argument_types === table.name) - .map(({ fn }) => { - return `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}` - }), - ]} + ...columnsByTableId[table.id].map((column) => + generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: column.is_nullable, + is_optional: false, + }, + { types, schemas, tables, views } + ) + ), + ...schemaFunctions + .filter(({ fn }) => fn.argument_types === table.name) + .map(({ fn }) => { + return `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}` + }), + ]} } Insert: { ${columnsByTableId[table.id].map((column) => { - if (column.identity_generation === 'ALWAYS') { - return `${JSON.stringify(column.name)}?: never` - } - return generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: column.is_nullable, - is_optional: - column.is_nullable || - column.is_identity || - column.default_value !== null, - }, - { types, schemas, tables, views } - ) - })} + if (column.identity_generation === 'ALWAYS') { + return `${JSON.stringify(column.name)}?: never` + } + return generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: column.is_nullable, + is_optional: + column.is_nullable || + column.is_identity || + column.default_value !== null, + }, + { types, schemas, tables, views } + ) + })} } Update: { ${columnsByTableId[table.id].map((column) => { - if (column.identity_generation === 'ALWAYS') { - return `${JSON.stringify(column.name)}?: never` - } - - return generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: column.is_nullable, - is_optional: true, - }, - { types, schemas, tables, views } - ) - })} + if (column.identity_generation === 'ALWAYS') { + return `${JSON.stringify(column.name)}?: never` + } + + return generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: column.is_nullable, + is_optional: true, + }, + { types, schemas, tables, views } + ) + })} } Relationships: [ ${relationships.map(generateRelationshiptTsDefinition)} ] }` - ) - } + ) + } } Views: { - ${ - schemaViews.length === 0 - ? '[_ in never]: never' - : schemaViews.map( - ({ view, relationships }) => `${JSON.stringify(view.name)}: { + ${schemaViews.length === 0 + ? '[_ in never]: never' + : schemaViews.map( + ({ view, relationships }) => `${JSON.stringify(view.name)}: { Row: { ${[ - ...columnsByTableId[view.id].map((column) => - generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: column.is_nullable, - is_optional: false, - }, - { types, schemas, tables, views } - ) - ), - ...schemaFunctions - .filter(({ fn }) => fn.argument_types === view.name) - .map( - ({ fn }) => - `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}` - ), - ]} + ...columnsByTableId[view.id].map((column) => + generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: column.is_nullable, + is_optional: false, + }, + { types, schemas, tables, views } + ) + ), + ...schemaFunctions + .filter(({ fn }) => fn.argument_types === view.name) + .map( + ({ fn }) => + `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}` + ), + ]} } - ${ - view.is_updatable - ? `Insert: { + ${view.is_updatable + ? `Insert: { ${columnsByTableId[view.id].map((column) => { - if (!column.is_updatable) { - return `${JSON.stringify(column.name)}?: never` - } - return generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: true, - is_optional: true, - }, - { types, schemas, tables, views } - ) - })} + if (!column.is_updatable) { + return `${JSON.stringify(column.name)}?: never` + } + return generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: true, + is_optional: true, + }, + { types, schemas, tables, views } + ) + })} } Update: { ${columnsByTableId[view.id].map((column) => { - if (!column.is_updatable) { - return `${JSON.stringify(column.name)}?: never` - } - return generateColumnTsDefinition( - schema, - { - name: column.name, - format: column.format, - is_nullable: true, - is_optional: true, - }, - { types, schemas, tables, views } - ) - })} + if (!column.is_updatable) { + return `${JSON.stringify(column.name)}?: never` + } + return generateColumnTsDefinition( + schema, + { + name: column.name, + format: column.format, + is_nullable: true, + is_optional: true, + }, + { types, schemas, tables, views } + ) + })} } ` - : '' - }Relationships: [ + : '' + }Relationships: [ ${relationships.map(generateRelationshiptTsDefinition)} ] }` - ) - } + ) + } } Functions: { ${(() => { - if (schemaFunctions.length === 0) { - return '[_ in never]: never' - } - const schemaFunctionsGroupedByName = schemaFunctions.reduce( - (acc, curr) => { - acc[curr.fn.name] ??= [] - acc[curr.fn.name].push(curr) - return acc - }, - {} as Record - ) - for (const fnName in schemaFunctionsGroupedByName) { - schemaFunctionsGroupedByName[fnName].sort( - (a, b) => - a.fn.argument_types.localeCompare(b.fn.argument_types) || - a.fn.return_type.localeCompare(b.fn.return_type) - ) - } + if (schemaFunctions.length === 0) { + return '[_ in never]: never' + } + const schemaFunctionsGroupedByName = schemaFunctions.reduce( + (acc, curr) => { + acc[curr.fn.name] ??= [] + acc[curr.fn.name].push(curr) + return acc + }, + {} as Record + ) + for (const fnName in schemaFunctionsGroupedByName) { + schemaFunctionsGroupedByName[fnName].sort( + (a, b) => + a.fn.argument_types.localeCompare(b.fn.argument_types) || + a.fn.return_type.localeCompare(b.fn.return_type) + ) + } - return Object.entries(schemaFunctionsGroupedByName) - .map(([fnName, fns]) => { - const functionSignatures = getFunctionSignatures(schema, fns) - return `${JSON.stringify(fnName)}:\n${functionSignatures}` - }) - .join(',\n') - })()} + return Object.entries(schemaFunctionsGroupedByName) + .map(([fnName, fns]) => { + const functionSignatures = getFunctionSignatures(schema, fns) + return `${JSON.stringify(fnName)}:\n${functionSignatures}` + }) + .join(',\n') + })()} } 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 = typesById.get(type_id) - let tsType = 'unknown' - if (type) { - tsType = `${generateNullableUnionTsType( - pgTypeToTsType(schema, type.name, { - types, - schemas, - tables, - views, - }), - true - )}` - } - return `${JSON.stringify(name)}: ${tsType}` - })} + const type = typesById.get(type_id) + let tsType = 'unknown' + if (type) { + tsType = `${generateNullableUnionTsType( + pgTypeToTsType(schema, type.name, { + types, + schemas, + tables, + views, + }), + true + )}` + } + return `${JSON.stringify(name)}: ${tsType}` + })} }` - ) - } + ) + } } }` })} @@ -850,11 +842,11 @@ export const Constants = { return `${JSON.stringify(schema.name)}: { Enums: { ${schemaEnums.map( - (enum_) => - `${JSON.stringify(enum_.name)}: [${enum_.enums - .map((variant) => JSON.stringify(variant)) - .join(', ')}]` - )} + (enum_) => + `${JSON.stringify(enum_.name)}: [${enum_.enums + .map((variant) => JSON.stringify(variant)) + .join(', ')}]` + )} } }` })} diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 7bb7013b..b396cd33 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -549,52 +549,36 @@ test('typegen: typescript', async () => { } blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } blurb_varchar: | { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } | { Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } created_ago: { Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } - Returns: { - error: true - } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } days_since_event: { Args: { "": Database["public"]["Tables"]["events"]["Row"] } - Returns: { - error: true - } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: boolean } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string[] } double_duration: { Args: { @@ -603,7 +587,7 @@ test('typegen: typescript', async () => { Returns: string } function_returning_row: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -619,7 +603,7 @@ test('typegen: typescript', async () => { } } function_returning_set_of_rows: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -651,7 +635,7 @@ test('typegen: typescript', async () => { } } function_returning_table: { - Args: never + Args: Record Returns: { id: number name: string @@ -693,7 +677,7 @@ test('typegen: typescript', async () => { } } get_composite_type_data: { - Args: never + Args: Record Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] SetofOptions: { from: "*" @@ -856,19 +840,19 @@ test('typegen: typescript', async () => { isSetofReturn: true } } - get_user_ids: { Args: never; Returns: number[] } - get_user_summary: { Args: never; Returns: Record[] } + get_user_ids: { Args: Record; Returns: number[] } + get_user_summary: { Args: Record; Returns: Record[] } polymorphic_function: { Args: { "": string }; Returns: undefined } polymorphic_function_with_different_return: { Args: { "": string } Returns: string } polymorphic_function_with_no_params_or_unnamed: - | { Args: never; Returns: number } + | { Args: Record; Returns: number } | { Args: { "": string }; Returns: string } polymorphic_function_with_unnamed_default: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -876,7 +860,7 @@ test('typegen: typescript', async () => { | { Args: { ""?: string }; Returns: string } polymorphic_function_with_unnamed_default_overload: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -895,14 +879,14 @@ test('typegen: typescript', async () => { Returns: number } postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } - postgres_fdw_disconnect_all: { Args: never; Returns: boolean } + postgres_fdw_disconnect_all: { Args: Record; Returns: boolean } postgres_fdw_get_connections: { - Args: never + Args: Record Returns: Record[] } - postgres_fdw_handler: { Args: never; Returns: unknown } + postgres_fdw_handler: { Args: Record; Returns: unknown } postgrest_resolvable_with_override_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: string }; Returns: number } | { Args: { b: number }; Returns: string } | { @@ -950,7 +934,7 @@ test('typegen: typescript', async () => { } } postgrest_unresolvable_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: number } Returns: { @@ -977,7 +961,7 @@ test('typegen: typescript', async () => { isSetofReturn: true } } - test_internal_query: { Args: never; Returns: undefined } + test_internal_query: { Args: Record; Returns: undefined } test_unnamed_row_composite: { Args: { "": Database["public"]["Tables"]["users"]["Row"] } Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] @@ -1828,7 +1812,7 @@ test('typegen w/ one-to-one relationships', async () => { Returns: string } function_returning_row: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -1844,7 +1828,7 @@ test('typegen w/ one-to-one relationships', async () => { } } function_returning_set_of_rows: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -1876,7 +1860,7 @@ test('typegen w/ one-to-one relationships', async () => { } } function_returning_table: { - Args: never + Args: Record Returns: { id: number name: string @@ -1918,7 +1902,7 @@ test('typegen w/ one-to-one relationships', async () => { } } get_composite_type_data: { - Args: never + Args: Record Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] SetofOptions: { from: "*" @@ -2081,19 +2065,19 @@ test('typegen w/ one-to-one relationships', async () => { isSetofReturn: true } } - get_user_ids: { Args: never; Returns: number[] } - get_user_summary: { Args: never; Returns: Record[] } + get_user_ids: { Args: Record; Returns: number[] } + get_user_summary: { Args: Record; Returns: Record[] } polymorphic_function: { Args: { "": string }; Returns: undefined } polymorphic_function_with_different_return: { Args: { "": string } Returns: string } polymorphic_function_with_no_params_or_unnamed: - | { Args: never; Returns: number } + | { Args: Record; Returns: number } | { Args: { "": string }; Returns: string } polymorphic_function_with_unnamed_default: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -2101,7 +2085,7 @@ test('typegen w/ one-to-one relationships', async () => { | { Args: { ""?: string }; Returns: string } polymorphic_function_with_unnamed_default_overload: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -2120,14 +2104,14 @@ test('typegen w/ one-to-one relationships', async () => { Returns: number } postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } - postgres_fdw_disconnect_all: { Args: never; Returns: boolean } + postgres_fdw_disconnect_all: { Args: Record; Returns: boolean } postgres_fdw_get_connections: { - Args: never + Args: Record Returns: Record[] } - postgres_fdw_handler: { Args: never; Returns: unknown } + postgres_fdw_handler: { Args: Record; Returns: unknown } postgrest_resolvable_with_override_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: string }; Returns: number } | { Args: { b: number }; Returns: string } | { @@ -2175,7 +2159,7 @@ test('typegen w/ one-to-one relationships', async () => { } } postgrest_unresolvable_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: number } Returns: { @@ -2202,7 +2186,7 @@ test('typegen w/ one-to-one relationships', async () => { isSetofReturn: true } } - test_internal_query: { Args: never; Returns: undefined } + test_internal_query: { Args: Record; Returns: undefined } test_unnamed_row_composite: { Args: { "": Database["public"]["Tables"]["users"]["Row"] } Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] @@ -2999,52 +2983,36 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } blurb_varchar: | { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } | { Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } created_ago: { Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } - Returns: { - error: true - } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } days_since_event: { Args: { "": Database["public"]["Tables"]["events"]["Row"] } - Returns: { - error: true - } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: boolean } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string[] } double_duration: { Args: { @@ -3053,7 +3021,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { Returns: string } function_returning_row: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -3069,7 +3037,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } function_returning_set_of_rows: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -3101,7 +3069,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } function_returning_table: { - Args: never + Args: Record Returns: { id: number name: string @@ -3143,7 +3111,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } get_composite_type_data: { - Args: never + Args: Record Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] SetofOptions: { from: "*" @@ -3306,19 +3274,19 @@ test('typegen: typescript w/ one-to-one relationships', async () => { isSetofReturn: true } } - get_user_ids: { Args: never; Returns: number[] } - get_user_summary: { Args: never; Returns: Record[] } + get_user_ids: { Args: Record; Returns: number[] } + get_user_summary: { Args: Record; Returns: Record[] } polymorphic_function: { Args: { "": string }; Returns: undefined } polymorphic_function_with_different_return: { Args: { "": string } Returns: string } polymorphic_function_with_no_params_or_unnamed: - | { Args: never; Returns: number } + | { Args: Record; Returns: number } | { Args: { "": string }; Returns: string } polymorphic_function_with_unnamed_default: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -3326,7 +3294,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { | { Args: { ""?: string }; Returns: string } polymorphic_function_with_unnamed_default_overload: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -3345,14 +3313,14 @@ test('typegen: typescript w/ one-to-one relationships', async () => { Returns: number } postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } - postgres_fdw_disconnect_all: { Args: never; Returns: boolean } + postgres_fdw_disconnect_all: { Args: Record; Returns: boolean } postgres_fdw_get_connections: { - Args: never + Args: Record Returns: Record[] } - postgres_fdw_handler: { Args: never; Returns: unknown } + postgres_fdw_handler: { Args: Record; Returns: unknown } postgrest_resolvable_with_override_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: string }; Returns: number } | { Args: { b: number }; Returns: string } | { @@ -3400,7 +3368,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } postgrest_unresolvable_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: number } Returns: { @@ -3427,7 +3395,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { isSetofReturn: true } } - test_internal_query: { Args: never; Returns: undefined } + test_internal_query: { Args: Record; Returns: undefined } test_unnamed_row_composite: { Args: { "": Database["public"]["Tables"]["users"]["Row"] } Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] @@ -4229,52 +4197,36 @@ test('typegen: typescript w/ postgrestVersion', async () => { } blurb: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } blurb_varchar: | { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } | { Args: { "": Database["public"]["Views"]["todos_view"]["Row"] } - Returns: { - error: true - } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string } created_ago: { Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } - Returns: { - error: true - } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } days_since_event: { Args: { "": Database["public"]["Tables"]["events"]["Row"] } - Returns: { - error: true - } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_is_long with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: boolean } details_length: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_length with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: number } details_words: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } - Returns: { - error: true - } & "the function public.details_words with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + Returns: string[] } double_duration: { Args: { @@ -4283,7 +4235,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { Returns: string } function_returning_row: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -4299,7 +4251,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } function_returning_set_of_rows: { - Args: never + Args: Record Returns: { decimal: number | null id: number @@ -4331,7 +4283,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } function_returning_table: { - Args: never + Args: Record Returns: { id: number name: string @@ -4373,7 +4325,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } get_composite_type_data: { - Args: never + Args: Record Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"][] SetofOptions: { from: "*" @@ -4536,19 +4488,19 @@ test('typegen: typescript w/ postgrestVersion', async () => { isSetofReturn: true } } - get_user_ids: { Args: never; Returns: number[] } - get_user_summary: { Args: never; Returns: Record[] } + get_user_ids: { Args: Record; Returns: number[] } + get_user_summary: { Args: Record; Returns: Record[] } polymorphic_function: { Args: { "": string }; Returns: undefined } polymorphic_function_with_different_return: { Args: { "": string } Returns: string } polymorphic_function_with_no_params_or_unnamed: - | { Args: never; Returns: number } + | { Args: Record; Returns: number } | { Args: { "": string }; Returns: string } polymorphic_function_with_unnamed_default: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default(), public.polymorphic_function_with_unnamed_default( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -4556,7 +4508,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { | { Args: { ""?: string }; Returns: string } polymorphic_function_with_unnamed_default_overload: | { - Args: never + Args: Record Returns: { error: true } & "Could not choose the best candidate function between: public.polymorphic_function_with_unnamed_default_overload(), public.polymorphic_function_with_unnamed_default_overload( => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" @@ -4575,14 +4527,14 @@ test('typegen: typescript w/ postgrestVersion', async () => { Returns: number } postgres_fdw_disconnect: { Args: { "": string }; Returns: boolean } - postgres_fdw_disconnect_all: { Args: never; Returns: boolean } + postgres_fdw_disconnect_all: { Args: Record; Returns: boolean } postgres_fdw_get_connections: { - Args: never + Args: Record Returns: Record[] } - postgres_fdw_handler: { Args: never; Returns: unknown } + postgres_fdw_handler: { Args: Record; Returns: unknown } postgrest_resolvable_with_override_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: string }; Returns: number } | { Args: { b: number }; Returns: string } | { @@ -4630,7 +4582,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } postgrest_unresolvable_function: - | { Args: never; Returns: undefined } + | { Args: Record; Returns: undefined } | { Args: { a: number } Returns: { @@ -4657,7 +4609,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { isSetofReturn: true } } - test_internal_query: { Args: never; Returns: undefined } + test_internal_query: { Args: Record; Returns: undefined } test_unnamed_row_composite: { Args: { "": Database["public"]["Tables"]["users"]["Row"] } Returns: Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] diff --git a/test/server/typegen_repro.test.ts b/test/server/typegen_repro.test.ts new file mode 100644 index 00000000..ce4019a6 --- /dev/null +++ b/test/server/typegen_repro.test.ts @@ -0,0 +1,200 @@ + +import { test, expect } from 'vitest' +import { apply } from '../../src/server/templates/typescript' + +test('repro: typescript generation issues', async () => { + const metadata = { + schemas: [{ id: 1, name: 'public', owner: 'postgres' }], + tables: [ + { + id: 1, + schema: 'public', + name: 'products', + is_rls_enabled: false, + primary_keys: [{ schema: 'public', table_name: 'products', name: 'id' }], + required_for_insert: [], + }, + ], + views: [], + foreignTables: [], + materializedViews: [], + columns: [ + { + table_id: 1, + schema: 'public', + table: 'products', + id: '1', + ordinal_position: 1, + name: 'id', + default_value: null, + data_type: 'integer', + format: 'int4', + is_identity: true, + identity_generation: 'BY DEFAULT', + is_nullable: false, + is_updatable: true, + is_unique: false, + enums: [], + comment: null, + check: null, + is_generated: false + }, + { + table_id: 1, + schema: 'public', + table: 'products', + id: '2', + ordinal_position: 2, + name: 'name', + default_value: null, + data_type: 'text', + format: 'text', + is_identity: false, + identity_generation: null, + is_nullable: false, + is_updatable: true, + is_unique: false, + enums: [], + comment: null, + check: null, + is_generated: false + }, + ], + relationships: [], + functions: [ + { + id: 101, + schema: 'public', + name: 'zero_arg_fn', + language: 'plpgsql', + definition: '...', + return_type_id: 23, // int4 + return_type_relation_id: null, + return_type: 'int4', + args: [], + argument_types: '', // No arguments + is_set_returning_function: false, + behavior: 'VOLATILE', + security_definer: false, + config_params: null, + complete_statement: '', + identity_argument_types: '', + prorows: null + }, + { + id: 102, + schema: 'public', + name: 'name_translated', + language: 'plpgsql', + definition: '...', + return_type_id: 25, // text + return_type_relation_id: null, + return_type: 'text', + args: [ + { + mode: 'in', + name: '', + type_id: 10001, // arbitrary type id for table products row + has_default: false, + }, + ], + // This is crucial: identifying it as taking 'products' as argument + argument_types: 'products', + is_set_returning_function: false, + behavior: 'STABLE', + security_definer: false, + config_params: null, + complete_statement: '', + identity_argument_types: 'products', + prorows: null + }, + { + id: 103, + schema: 'public', + name: 'fn_with_single_unnamed_table_arg', + language: 'plpgsql', + definition: '...', + return_type_id: 25, // text + return_type_relation_id: null, + return_type: 'text', + args: [ + { + mode: 'in', + name: '', + type_id: 10001, + has_default: false, + }, + ], + argument_types: 'products', + is_set_returning_function: false, + behavior: 'STABLE', + security_definer: false, + config_params: null, + complete_statement: '', + identity_argument_types: 'products', + prorows: null + } + ], + types: [ + { + id: 23, + name: 'int4', + schema: 'pg_catalog', + format: 'int4', + enums: [], + attributes: [], + comment: null, + type_relation_id: null + }, + { + id: 25, + name: 'text', + schema: 'pg_catalog', + format: 'text', + enums: [], + attributes: [], + comment: null, + type_relation_id: null + }, + // Mock type for the products table row + { + id: 10001, + name: 'products', + schema: 'public', + format: 'products', + enums: [], + attributes: [], + comment: null, + type_relation_id: 1 // Link to table id + } + ], + detectOneToOneRelationships: false, + } as any + + const output = await apply(metadata) + // console.log(output) + require('fs').writeFileSync('repro_output.txt', output) + + // 1. Check zero arg function + // Use regex to be resilient to whitespace changes + const zeroArgRegex = /"zero_arg_fn":\s*\{\s*Args: Record\s*Returns: number\s*\}/ + // Use regex to be resilient to whitespace changes and Prettier quote removal + expect(output).toMatch(/["']?zero_arg_fn["']?:/) + expect(output).toContain('Args: Record') + // expect(output).toMatch(zeroArgRegex) + + // 2. Check computed field in Table Row + // It should appear in Tables.products.Row + // We expect: "name_translated": string | null + // We expect: "name_translated": string | null + // allowing for optional quotes on keys + const tableRowRegex = /["']?products["']?: \{\s*Row: \{[\s\S]*?["']?name_translated["']?: string \| null/ + expect(output).toMatch(tableRowRegex) + + // 3. Check function with single unnamed table arg in + // Functions + // Use flexible regex to handle formatting + const fnRegex = /["']?fn_with_single_unnamed_table_arg["']?:\s*\{\s*Args:\s*\{\s*""\s*:\s*Database\["public"\]\["Tables"\]\["products"\]\["Row"\]\s*\}\s*Returns:\s*string\s*\}/ + expect(output).toMatch(fnRegex) + +}) diff --git a/test_error.txt b/test_error.txt new file mode 100644 index 00000000..a3e7d9bd Binary files /dev/null and b/test_error.txt differ diff --git a/test_output.txt b/test_output.txt new file mode 100644 index 00000000..921256ef Binary files /dev/null and b/test_output.txt differ diff --git a/test_output_utf8.txt b/test_output_utf8.txt new file mode 100644 index 00000000..d2db6fce --- /dev/null +++ b/test_output_utf8.txt @@ -0,0 +1,198 @@ + + RUN v3.0.9 D:/Open Source/k/postgres-meta + +stdout | test/server/typegen_repro.test.ts > repro: typescript generation issues +Processing function: public.zero_arg_fn. In schema cache: true. inArgs: 0 +Processing function: public.name_translated. In schema cache: true. inArgs: 1 +Processing function: public.fn_with_single_unnamed_table_arg. In schema cache: true. inArgs: 1 + +stdout | test/server/typegen_repro.test.ts > repro: typescript generation issues +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + public: { + Tables: { + products: { + Row: { + id: number + name: string + fn_with_single_unnamed_table_arg: string | null + name_translated: string | null + } + Insert: { + id?: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + fn_with_single_unnamed_table_arg: { + Args: { "": Database["public"]["Tables"]["products"]["Row"] } + Returns: { + error: true + } & "the function public.fn_with_single_unnamed_table_arg with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + name_translated: { + Args: { "": Database["public"]["Tables"]["products"]["Row"] } + Returns: { + error: true + } & "the function public.name_translated with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + zero_arg_fn: { Args: Record; Returns: number } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const + + + Γ¥» test/server/typegen_repro.test.ts (1 test | 1 failed) 171ms + ├ù repro: typescript generation issues 170ms + ΓåÆ expected 'export type Json =\n | string\n | nΓǪ' to contain '"zero_arg_fn"' + + Test Files 1 failed (1) + Tests 1 failed (1) + Start at 10:04:19 + Duration 891ms (transform 112ms, setup 0ms, collect 180ms, tests 171ms, environment 0ms, prepare 157ms) + diff --git a/test_update_error.txt b/test_update_error.txt new file mode 100644 index 00000000..57c7c0bc Binary files /dev/null and b/test_update_error.txt differ