|  | 
|  | 1 | +import { type, type Type } from 'arktype'; | 
|  | 2 | +import type { | 
|  | 3 | +  Assume, | 
|  | 4 | +  Column, | 
|  | 5 | +  DrizzleTypeError, | 
|  | 6 | +  Equal, | 
|  | 7 | +  Simplify, | 
|  | 8 | +  Table, | 
|  | 9 | +} from 'drizzle-orm'; | 
|  | 10 | +import { getTableColumns, is } from 'drizzle-orm'; | 
|  | 11 | +import { | 
|  | 12 | +  PgArray, | 
|  | 13 | +  PgDate, | 
|  | 14 | +  PgTimestamp, | 
|  | 15 | +  PgUUID, | 
|  | 16 | +  PgVarchar, | 
|  | 17 | +  pgTable, | 
|  | 18 | +  text, | 
|  | 19 | +  uuid, | 
|  | 20 | +} from 'drizzle-orm/pg-core'; | 
|  | 21 | + | 
|  | 22 | +const literalSchema = type('string | number | boolean | null'); | 
|  | 23 | + | 
|  | 24 | +type Literal = typeof literalSchema.infer; | 
|  | 25 | + | 
|  | 26 | +type Json = Literal | { [key: string]: Json }; | 
|  | 27 | + | 
|  | 28 | +type MapInsertColumnsToArk< | 
|  | 29 | +  TColumn extends Column, | 
|  | 30 | +  TType extends Type<any>, | 
|  | 31 | +> = TColumn['_']['notNull'] extends false | 
|  | 32 | +  ? Type<Partial<TType>> | 
|  | 33 | +  : TColumn['_']['hasDefault'] extends true | 
|  | 34 | +    ? Type<Partial<TType>> | 
|  | 35 | +    : TType; | 
|  | 36 | + | 
|  | 37 | +type MapSelectColumnToArk< | 
|  | 38 | +  TColumn extends Column, | 
|  | 39 | +  TType extends Type<any>, | 
|  | 40 | +> = TColumn['_']['notNull'] extends false ? Type<Partial<TType>> : TType; | 
|  | 41 | + | 
|  | 42 | +type MapColumnToArk< | 
|  | 43 | +  TColumn extends Column, | 
|  | 44 | +  TType extends Type<any>, | 
|  | 45 | +  TMode extends 'insert' | 'select', | 
|  | 46 | +> = TMode extends 'insert' | 
|  | 47 | +  ? MapInsertColumnsToArk<TColumn, TType> | 
|  | 48 | +  : MapSelectColumnToArk<TColumn, TType>; | 
|  | 49 | + | 
|  | 50 | +type MaybeOptional< | 
|  | 51 | +  TColumn extends Column, | 
|  | 52 | +  TType extends Type<any>, | 
|  | 53 | +  TMode extends 'insert' | 'select', | 
|  | 54 | +  TNoOptional extends boolean, | 
|  | 55 | +> = TNoOptional extends true ? TType : MapColumnToArk<TColumn, TType, TMode>; | 
|  | 56 | + | 
|  | 57 | +let a = arrayOf('number | string'); | 
|  | 58 | + | 
|  | 59 | +type GetArkType<TColumn extends Column> = | 
|  | 60 | +  TColumn['_']['dataType'] extends infer TDataType | 
|  | 61 | +    ? TDataType extends 'custom' | 
|  | 62 | +      ? PrecompiledDefaults['any'] | 
|  | 63 | +      : TDataType extends 'json' | 
|  | 64 | +        ? Type<Json> | 
|  | 65 | +        : TColumn extends { enumValues: [string, ...string[]] } | 
|  | 66 | +          ? Equal<TColumn['enumValues'], [string, ...string[]]> extends true | 
|  | 67 | +            ? PrecompiledDefaults['string'] | 
|  | 68 | +            : Type<TColumn['enumValues']> | 
|  | 69 | +          : TDataType extends 'array' | 
|  | 70 | +            ? GetArkType< | 
|  | 71 | +                Assume<TColumn['_'], { baseColumn: Column }>['baseColumn'] | 
|  | 72 | +              >[] | 
|  | 73 | +            : TDataType extends 'bigint' | 
|  | 74 | +              ? PrecompiledDefaults['bigint'] | 
|  | 75 | +              : TDataType extends 'number' | 
|  | 76 | +                ? PrecompiledDefaults['number'] | 
|  | 77 | +                : TDataType extends 'string' | 
|  | 78 | +                  ? PrecompiledDefaults['string'] | 
|  | 79 | +                  : TDataType extends 'boolean' | 
|  | 80 | +                    ? PrecompiledDefaults['boolean'] | 
|  | 81 | +                    : TDataType extends 'date' | 
|  | 82 | +                      ? PrecompiledDefaults['Date'] | 
|  | 83 | +                      : PrecompiledDefaults['any'] | 
|  | 84 | +    : never; | 
|  | 85 | + | 
|  | 86 | +type ValueOrUpdater<T, TUpdaterArg> = T | ((arg: TUpdaterArg) => T); | 
|  | 87 | + | 
|  | 88 | +type UnwrapValueOrUpdater<T> = | 
|  | 89 | +  T extends ValueOrUpdater<infer U, any> ? U : never; | 
|  | 90 | + | 
|  | 91 | +type Refine<TTable extends Table, TMode extends 'select' | 'insert'> = { | 
|  | 92 | +  [K in keyof TTable['_']['columns']]?: ValueOrUpdater< | 
|  | 93 | +    PrecompiledDefaults['any'], | 
|  | 94 | +    TMode extends 'select' | 
|  | 95 | +      ? BuildSelectSchema<TTable, {}, true> | 
|  | 96 | +      : BuildInsertSchema<TTable, {}, true> | 
|  | 97 | +  >; | 
|  | 98 | +}; | 
|  | 99 | + | 
|  | 100 | +type BuildSelectSchema< | 
|  | 101 | +  TTable extends Table, | 
|  | 102 | +  TRefine extends Refine<TTable, 'select'>, | 
|  | 103 | +  TNoOptional extends boolean = false, | 
|  | 104 | +> = Simplify<{ | 
|  | 105 | +  [K in keyof TTable['_']['columns']]: MaybeOptional< | 
|  | 106 | +    TTable['_']['columns'][K], | 
|  | 107 | +    K extends keyof TRefine | 
|  | 108 | +      ? Assume<UnwrapValueOrUpdater<TRefine[K]>, PrecompiledDefaults['any']> | 
|  | 109 | +      : GetArkType<TTable['_']['columns'][K]>, | 
|  | 110 | +    'select', | 
|  | 111 | +    TNoOptional | 
|  | 112 | +  >; | 
|  | 113 | +}>; | 
|  | 114 | + | 
|  | 115 | +export type BuildInsertSchema< | 
|  | 116 | +  TTable extends Table, | 
|  | 117 | +  TRefine extends Refine<TTable, 'insert'> | {}, | 
|  | 118 | +  TNoOptional extends boolean = false, | 
|  | 119 | +> = TTable['_']['columns'] extends infer TColumns extends Record< | 
|  | 120 | +  string, | 
|  | 121 | +  Column<any> | 
|  | 122 | +> | 
|  | 123 | +  ? { | 
|  | 124 | +      [K in keyof TColumns & string]: MaybeOptional< | 
|  | 125 | +        TColumns[K], | 
|  | 126 | +        K extends keyof TRefine | 
|  | 127 | +          ? Assume<UnwrapValueOrUpdater<TRefine[K]>, PrecompiledDefaults['any']> | 
|  | 128 | +          : GetArkType<TColumns[K]>, | 
|  | 129 | +        'insert', | 
|  | 130 | +        TNoOptional | 
|  | 131 | +      >; | 
|  | 132 | +    } | 
|  | 133 | +  : never; | 
|  | 134 | + | 
|  | 135 | +const exampleTable = pgTable('something', { | 
|  | 136 | +  id: uuid('id').primaryKey(), | 
|  | 137 | +  name: text('name').array(), | 
|  | 138 | +}); | 
|  | 139 | + | 
|  | 140 | +export function createInsertSchema< | 
|  | 141 | +  TTable extends Table, | 
|  | 142 | +  TRefine extends Refine<TTable, 'insert'> = Refine<TTable, 'insert'>, | 
|  | 143 | +>( | 
|  | 144 | +  table: TTable, | 
|  | 145 | +  /** | 
|  | 146 | +   * @param refine Refine schema fields | 
|  | 147 | +   */ | 
|  | 148 | +  refine?: { | 
|  | 149 | +    [K in keyof TRefine]: K extends keyof TTable['_']['columns'] | 
|  | 150 | +      ? TRefine[K] | 
|  | 151 | +      : DrizzleTypeError<`Column '${K & string}' does not exist in table '${TTable['_']['name']}'`>; | 
|  | 152 | +  }, | 
|  | 153 | +): Type< | 
|  | 154 | +  BuildInsertSchema< | 
|  | 155 | +    TTable, | 
|  | 156 | +    Equal<TRefine, Refine<TTable, 'insert'>> extends true ? {} : TRefine | 
|  | 157 | +  > | 
|  | 158 | +> { | 
|  | 159 | +  const columns = getTableColumns(table); | 
|  | 160 | +  const columnEntries = Object.entries(columns); | 
|  | 161 | + | 
|  | 162 | +  const map = Object.fromEntries( | 
|  | 163 | +    columnEntries.map(([key, value]) => columnToArkType(key, value)), | 
|  | 164 | +  ); | 
|  | 165 | + | 
|  | 166 | +  return type(map); | 
|  | 167 | +} | 
|  | 168 | + | 
|  | 169 | +function columnToArkType(key: string, column: Column) { | 
|  | 170 | +  // console.info(column); | 
|  | 171 | +  let mappedKey = key; | 
|  | 172 | + | 
|  | 173 | +  if (!column.notNull || column.hasDefault) mappedKey = `${mappedKey}?`; | 
|  | 174 | + | 
|  | 175 | +  return [mappedKey, mapToArkType(column)]; | 
|  | 176 | +} | 
|  | 177 | + | 
|  | 178 | +export function createUpdateSchema() {} | 
|  | 179 | + | 
|  | 180 | +export function mapToArkType( | 
|  | 181 | +  column: Column, | 
|  | 182 | +): keyof PrecompiledDefaults | string { | 
|  | 183 | +  if (is(column, PgUUID)) return 'uuid'; | 
|  | 184 | + | 
|  | 185 | +  if (is(column, PgDate) || is(column, PgTimestamp)) return 'Date'; | 
|  | 186 | + | 
|  | 187 | +  if (column.dataType === 'array') | 
|  | 188 | +    return mapToArkType((column as PgArray<any, any>).baseColumn); | 
|  | 189 | + | 
|  | 190 | +  if (column.dataType === 'string' && column.enumValues) | 
|  | 191 | +    return `${column.enumValues.map(v => `'${v}'`).join(' | ')}`; | 
|  | 192 | + | 
|  | 193 | +  if (is(column, PgVarchar) && column.length) | 
|  | 194 | +    return `${column.dataType}<=${column.length}`; | 
|  | 195 | + | 
|  | 196 | +  return column.dataType as keyof PrecompiledDefaults; | 
|  | 197 | +} | 
0 commit comments