Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions drizzle-arktype/benchmarks/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { bench, setup } from '@ark/attest';
import { type } from 'arktype';
import { boolean, integer, pgTable, text } from 'drizzle-orm/pg-core';
import { createSelectSchema } from '~/index.ts';

const users = pgTable('users', {
id: integer().primaryKey(),
firstName: text().notNull(),
middleName: text(),
lastName: text().notNull(),
age: integer().notNull(),
admin: boolean().notNull().default(false),
});

const teardown = setup();

bench('select schema', () => {
return createSelectSchema(users);
}).types([13129, 'instantiations']);

bench('select schema with refinements', () => {
return createSelectSchema(users, {
firstName: (t) => t.atMostLength(100),
middleName: (t) => t.atMostLength(100),
lastName: (t) => t.atMostLength(100),
age: type.number.atLeast(1),
});
}).types([21631, 'instantiations']);

teardown();
5 changes: 4 additions & 1 deletion drizzle-arktype/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"test:types": "cd tests && tsc",
"pack": "(cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz",
"publish": "npm publish package.tgz",
"test": "vitest run"
"test": "vitest run",
"bench:types": "tsx ./benchmarks/types.ts"
},
"exports": {
".": {
Expand Down Expand Up @@ -59,6 +60,7 @@
"drizzle-orm": ">=0.36.0"
},
"devDependencies": {
"@ark/attest": "^0.45.8",
"@rollup/plugin-typescript": "^11.1.0",
"@types/node": "^18.15.10",
"arktype": "^2.1.10",
Expand All @@ -67,6 +69,7 @@
"json-rules-engine": "7.3.0",
"rimraf": "^5.0.0",
"rollup": "^3.20.7",
"tsx": "^4.19.3",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
"zx": "^7.2.2"
Expand Down
4 changes: 3 additions & 1 deletion drizzle-arktype/src/column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ import { isColumnType, isWithEnum } from './utils.ts';

export const literalSchema = type.string.or(type.number).or(type.boolean).or(type.null);
export const jsonSchema = literalSchema.or(type.unknown.as<any>().array()).or(type.object.as<Record<string, any>>());
export const bufferSchema = type.instanceOf(Buffer); // eslint-disable-line no-instanceof/no-instanceof
export const bufferSchema = type.unknown.narrow((value) => value instanceof Buffer).as<Buffer>().describe( // eslint-disable-line no-instanceof/no-instanceof
'a Buffer instance',
);
Comment on lines +62 to +64
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @L-Mario564, I just wanted to let you know that I did a quick test for this specific schema in my codebase where I encountered the problem, and it does solve it. Thank you very much!


export function columnToSchema(column: Column): Type {
let schema!: Type;
Expand Down
45 changes: 16 additions & 29 deletions drizzle-arktype/src/column.types.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import { Type, type } from 'arktype';
import type { Assume, Column } from 'drizzle-orm';
import type { ColumnIsGeneratedAlwaysAs, IsEnumDefined, IsNever, IsUnknown, Json } from './utils.ts';
import type { Column } from 'drizzle-orm';
import type { Json } from './utils.ts';

export type ArktypeNullable<TSchema> = Type<type.infer<TSchema> | null, {}>;
export type ArktypeNullable<TSchema> = Type<type.infer<TSchema> | null>;

export type ArktypeOptional<TSchema> = [Type<type.infer<TSchema>, {}>, '?'];

export type GetEnumValuesFromColumn<TColumn extends Column> = TColumn['_'] extends { enumValues: [string, ...string[]] }
? TColumn['_']['enumValues']
: undefined;
export type ArktypeOptional<TSchema> = [Type<type.infer<TSchema>>, '?'];

export type GetArktypeType<
TData,
TColumnType extends string,
TEnumValues extends [string, ...string[]] | undefined,
> = IsEnumDefined<TEnumValues> extends true ? Type<Assume<TEnumValues, any[]>[number]>
: TColumnType extends 'PgJson' | 'PgJsonb' | 'MySqlJson' | 'SingleStoreJson' | 'SQLiteTextJson' | 'SQLiteBlobJson'
? IsUnknown<TData> extends true ? Type<Json> : Type<TData>
: Type<TData>;
TColumn extends Column,
> = TColumn['_']['columnType'] extends
'PgJson' | 'PgJsonb' | 'MySqlJson' | 'SingleStoreJson' | 'SQLiteTextJson' | 'SQLiteBlobJson'
? unknown extends TColumn['_']['data'] ? Type<Json> : Type<TColumn['_']['data']>
: Type<TColumn['_']['data']>;

type HandleSelectColumn<
TSchema,
Expand All @@ -28,27 +22,20 @@ type HandleSelectColumn<
type HandleInsertColumn<
TSchema,
TColumn extends Column,
> = ColumnIsGeneratedAlwaysAs<TColumn> extends true ? never
: TColumn['_']['notNull'] extends true ? TColumn['_']['hasDefault'] extends true ? ArktypeOptional<TSchema>
: TSchema
> = TColumn['_']['notNull'] extends true ? TColumn['_']['hasDefault'] extends true ? ArktypeOptional<TSchema>
: TSchema
: ArktypeOptional<ArktypeNullable<TSchema>>;

type HandleUpdateColumn<
TSchema,
TColumn extends Column,
> = ColumnIsGeneratedAlwaysAs<TColumn> extends true ? never
: TColumn['_']['notNull'] extends true ? ArktypeOptional<TSchema>
> = TColumn['_']['notNull'] extends true ? ArktypeOptional<TSchema>
: ArktypeOptional<ArktypeNullable<TSchema>>;

export type HandleColumn<
TType extends 'select' | 'insert' | 'update',
TColumn extends Column,
> = GetArktypeType<
TColumn['_']['data'],
TColumn['_']['columnType'],
GetEnumValuesFromColumn<TColumn>
> extends infer TSchema ? TType extends 'select' ? HandleSelectColumn<TSchema, TColumn>
: TType extends 'insert' ? HandleInsertColumn<TSchema, TColumn>
: TType extends 'update' ? HandleUpdateColumn<TSchema, TColumn>
: TSchema
: Type;
> = TType extends 'select' ? HandleSelectColumn<GetArktypeType<TColumn>, TColumn>
: TType extends 'insert' ? HandleInsertColumn<GetArktypeType<TColumn>, TColumn>
: TType extends 'update' ? HandleUpdateColumn<GetArktypeType<TColumn>, TColumn>
: GetArktypeType<TColumn>;
1 change: 0 additions & 1 deletion drizzle-arktype/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export { bufferSchema, jsonSchema, literalSchema } from './column.ts';
export * from './column.types.ts';
export * from './schema.ts';
export type { BuildSchema } from './schema.types.internal.ts';
export * from './schema.types.internal.ts';
export * from './schema.types.ts';
export * from './utils.ts';
77 changes: 24 additions & 53 deletions drizzle-arktype/src/schema.types.internal.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import type { Type, type } from 'arktype';
import type { Assume, Column, DrizzleTypeError, SelectedFieldsFlat, Simplify, Table, View } from 'drizzle-orm';
import type {
ArktypeNullable,
ArktypeOptional,
GetArktypeType,
GetEnumValuesFromColumn,
HandleColumn,
} from './column.types.ts';
import type { GetSelection, RemoveNever } from './utils.ts';
import type { Column, DrizzleTypeError, SelectedFieldsFlat, Simplify, Table, View } from 'drizzle-orm';
import type { ArktypeNullable, ArktypeOptional, GetArktypeType, HandleColumn } from './column.types.ts';
import type { ColumnIsGeneratedAlwaysAs, GetSelection } from './utils.ts';

export interface Conditions {
never: (column?: Column) => boolean;
Expand All @@ -17,48 +11,32 @@ export interface Conditions {

type GenericSchema = type.cast<unknown> | [type.cast<unknown>, '?'];

export type BuildRefineColumns<
TColumns extends Record<string, any>,
> = Simplify<
RemoveNever<
{
[K in keyof TColumns]: TColumns[K] extends infer TColumn extends Column ? GetArktypeType<
TColumn['_']['data'],
TColumn['_']['columnType'],
GetEnumValuesFromColumn<TColumn>
> extends infer TSchema extends GenericSchema ? TSchema
: Type<any, {}>
: TColumns[K] extends infer TObject extends SelectedFieldsFlat<Column> | Table | View
? BuildRefineColumns<GetSelection<TObject>>
: TColumns[K];
}
>
>;
type BuildRefineField<T> = T extends GenericSchema ? ((schema: T) => GenericSchema) | GenericSchema : never;

export type BuildRefine<
TColumns extends Record<string, any>,
> = BuildRefineColumns<TColumns> extends infer TBuildColumns ? {
[K in keyof TBuildColumns]?: TBuildColumns[K] extends GenericSchema
? ((schema: TBuildColumns[K]) => GenericSchema) | GenericSchema
: TBuildColumns[K] extends Record<string, any> ? Simplify<BuildRefine<TBuildColumns[K]>>
: never;
}
: never;
> = {
[K in keyof TColumns as TColumns[K] extends Column | SelectedFieldsFlat<Column> | Table | View ? K : never]?:
TColumns[K] extends Column ? BuildRefineField<GetArktypeType<TColumns[K]>>
: BuildRefine<GetSelection<TColumns[K]>>;
};

type HandleRefinement<
TType extends 'select' | 'insert' | 'update',
TRefinement extends GenericSchema | ((schema: GenericSchema) => GenericSchema),
TRefinement,
TColumn extends Column,
> = TRefinement extends (schema: any) => GenericSchema ? (
TColumn['_']['notNull'] extends true ? ReturnType<TRefinement>
: ArktypeNullable<ReturnType<TRefinement>>
) extends infer TSchema ? TType extends 'update' ? ArktypeOptional<TSchema>
: TSchema
: Type<any, {}>
: Type<any>
: TRefinement;

type IsRefinementDefined<TRefinements, TKey extends string> = TKey extends keyof TRefinements
? TRefinements[TKey] extends GenericSchema | ((schema: any) => any) ? true
type IsRefinementDefined<
TRefinements extends Record<string | symbol | number, any> | undefined,
TKey extends string | symbol | number,
> = TRefinements extends object ? TRefinements[TKey] extends GenericSchema | ((schema: any) => any) ? true
: false
: false;

Expand All @@ -68,26 +46,19 @@ export type BuildSchema<
TRefinements extends Record<string, any> | undefined,
> = type.instantiate<
Simplify<
RemoveNever<
{
readonly [K in keyof TColumns]: TColumns[K] extends infer TColumn extends Column
? TRefinements extends object
? IsRefinementDefined<TRefinements, Assume<K, string>> extends true
? HandleRefinement<TType, TRefinements[Assume<K, keyof TRefinements>], TColumn>
: HandleColumn<TType, TColumn>
{
readonly [K in keyof TColumns as ColumnIsGeneratedAlwaysAs<TColumns[K]> extends true ? never : K]:
TColumns[K] extends infer TColumn extends Column
? IsRefinementDefined<TRefinements, K> extends true
? HandleRefinement<TType, TRefinements[K & keyof TRefinements], TColumn>
: HandleColumn<TType, TColumn>
: TColumns[K] extends infer TObject extends SelectedFieldsFlat<Column> | Table | View ? BuildSchema<
: TColumns[K] extends infer TNested extends SelectedFieldsFlat<Column> | Table | View ? BuildSchema<
TType,
GetSelection<TObject>,
TRefinements extends object
? TRefinements[Assume<K, keyof TRefinements>] extends infer TNestedRefinements extends object
? TNestedRefinements
: undefined
: undefined
GetSelection<TNested>,
TRefinements extends object ? TRefinements[K & keyof TRefinements] : undefined
>
: any;
}
>
}
>
>;

Expand Down
2 changes: 1 addition & 1 deletion drizzle-arktype/src/schema.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface CreateSelectSchema {
refine: NoUnknownKeys<TRefine, TView['$inferSelect']>,
): BuildSchema<'select', TView['_']['selectedFields'], TRefine>;

<TEnum extends PgEnum<any>>(enum_: TEnum): Type<TEnum['enumValues'][number], {}>;
<TEnum extends PgEnum<any>>(enum_: TEnum): Type<TEnum['enumValues'][number]>;
}

export interface CreateInsertSchema {
Expand Down
25 changes: 5 additions & 20 deletions drizzle-arktype/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,12 @@ export const isPgEnum: (entity: any) => entity is PgEnum<[string, ...string[]]>
type Literal = type.infer<typeof literalSchema>;
export type Json = Literal | Record<string, any> | any[];

export type IsEnumDefined<TEnum extends [string, ...string[]] | undefined> = [string, ...string[]] extends TEnum ? false
: undefined extends TEnum ? false
: true;

export type IsNever<T> = [T] extends [never] ? true : false;

export type ColumnIsGeneratedAlwaysAs<TColumn extends Column> = TColumn['_']['identity'] extends 'always' ? true
: TColumn['_']['generated'] extends undefined ? false
: TColumn['_']['generated'] extends infer TGenerated extends { type: string }
? TGenerated['type'] extends 'byDefault' ? false
: true
: true;

export type RemoveNever<T> = {
[K in keyof T as T[K] extends never ? never : K]: T[K];
};
export type ColumnIsGeneratedAlwaysAs<TColumn> = TColumn extends Column
? TColumn['_']['identity'] extends 'always' ? true
: TColumn['_']['generated'] extends { type: 'byDefault' } | undefined ? false
: true
: false;

export type GetSelection<T extends SelectedFieldsFlat<Column> | Table | View> = T extends Table ? T['_']['columns']
: T extends View ? T['_']['selectedFields']
: T;

export type IsUnknown<T> = unknown extends T ? [T] extends [null] ? false
: true
: false;
2 changes: 1 addition & 1 deletion drizzle-arktype/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"~/*": ["src/*"]
}
},
"include": ["src", "*.ts"]
"include": ["src", "*.ts", "benchmarks"]
}
Loading