Comparing version 0.0.3 to 0.0.4
// Generated by dts-bundle-generator v9.5.1 | ||
export declare class Sql { | ||
static ops: { | ||
[key: string]: string; | ||
export type DeleteOptions = { | ||
/** force delete even with no where clause */ | ||
force?: boolean; | ||
where?: Fragment | Fragment[]; | ||
}; | ||
export declare class Schema { | ||
dialect: Dialect; | ||
constructor(dialect: Dialect); | ||
converters: { | ||
[key: string]: TypeConverter; | ||
}; | ||
static opKeys: string[]; | ||
static create(driver: Driver): { | ||
(strings: TemplateStringsArray | string, ...params: any[]): Fragment; | ||
ref<Table extends Constructor<any>>(cls: Table, as?: string): TypeRef<InstanceType<Table>>; | ||
refs<T extends readonly Constructor[]>(...classes_0: T): ConstructorToTypeRef<T>; | ||
join<JoinTables extends Constructor<any>[]>(...joinTables: JoinTables): SqlJoinBuilder<JoinTables>; | ||
sqlTableNames(schema?: string): string; | ||
sqlColumnDefinition(column: ColumnDefinition): string; | ||
sqlIndexDefinition(table: TableDefinition, column: ColumnDefinition): string; | ||
dropTable(table: ClassParam): string; | ||
createTable(table: ClassParam): string; | ||
insert(table: ClassParam, options?: { | ||
onlyProps?: string[]; | ||
}): string; | ||
update(table: ClassParam, options?: { | ||
onlyProps?: string[]; | ||
}): string; | ||
delete(table: ClassParam, options?: DeleteOptions): string; | ||
toDbBindings(table: ClassInstance): DbBinding[]; | ||
toDbObject(table: ClassInstance, options?: { | ||
onlyProps?: string[]; | ||
}): { | ||
[key: string]: DbBinding; | ||
}; | ||
} | ||
export type SelectOptions = { | ||
props?: string[]; | ||
columns?: string[]; | ||
sql?: Fragment | Fragment[]; | ||
export type Constructor<T = any> = new (...args: any[]) => T; | ||
export type First<T extends readonly any[]> = T extends [ | ||
infer F, | ||
...any[] | ||
] ? F extends Constructor<any> ? F : never : never; | ||
export type Last<T extends readonly any[]> = T extends [ | ||
...any[], | ||
infer L | ||
] ? L extends Constructor<any> ? L : never : never; | ||
export type ScalarValue = string | bigint | number | boolean | null | symbol; | ||
export type DbBinding = string | bigint | NodeJS.TypedArray | number | boolean | null | Record<string, string | bigint | NodeJS.TypedArray | number | boolean | null>; | ||
export interface ReflectMeta { | ||
name: string; | ||
$id: symbol; | ||
$type: { | ||
name: string; | ||
table?: TableDefinition; | ||
}; | ||
$props: [ | ||
{ | ||
name: string; | ||
column?: ColumnDefinition; | ||
} | ||
]; | ||
} | ||
export type ColumnType = "INTEGER" | "SMALLINT" | "BIGINT" | "DECIMAL" | "NUMERIC" | "REAL" | "FLOAT" | "DOUBLE" | "MONEY" | "DATE" | "DATETIME" | "TIME" | "TIMEZ" | "TIMESTAMP" | "TIMESTAMPZ" | "INTERVAL" | "BOOLEAN" | "UUID" | "BLOB" | "BYTES" | "BIT" | "TEXT" | "VARCHAR" | "NVARCHAR" | "CHAR" | "NCHAR" | "JSON" | "JSONB" | "XML"; | ||
export type DialectTypes = { | ||
native: ColumnType[]; | ||
map: Record<string, ColumnType[]>; | ||
}; | ||
export declare class SelectQuery<Tables extends Constructor<any>[]> extends WhereQuery<Tables> { | ||
protected _select: string[]; | ||
protected _skip: number | undefined; | ||
protected _take: number | undefined; | ||
select(options: SelectOptions | TemplateStringsArray | string | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
get hasSelect(): boolean; | ||
skip(rows?: number): this; | ||
take(rows?: number): this; | ||
limit(skip?: number, take?: number): this; | ||
buildSelect(): string; | ||
buildFrom(): string; | ||
buildGroupBy(): string; | ||
buildHaving(): string; | ||
buildLimit(): string; | ||
build(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
export type ClassParam = ReflectMeta | { | ||
constructor: ReflectMeta; | ||
} | Constructor<any>; | ||
export type ClassInstance = { | ||
constructor: ReflectMeta; | ||
} & Record<string, any> | Record<string, any>; | ||
export type TypeRef<T> = T & { | ||
$ref: { | ||
cls: Constructor<T>; | ||
as?: string; | ||
}; | ||
}; | ||
export type ConstructorToTypeRef<T extends readonly any[]> = { | ||
[K in keyof T]: T[K] extends new (...args: any[]) => infer R ? TypeRef<R> : never; | ||
}; | ||
export type ConstructorsToRefs<T extends Constructor<any>[]> = { | ||
[K in keyof T]: TypeRef<InstanceType<T[K]>>; | ||
}; | ||
export type TypeRefs<Tables extends Constructor<any>[]> = { | ||
[K in keyof Tables]: TypeRef<InstanceType<Tables[K]>>; | ||
}; | ||
export interface TableDefinition { | ||
name: string; | ||
alias?: string; | ||
} | ||
declare class UpdateQuery<Tables extends Constructor<any>[]> extends WhereQuery<Tables> { | ||
private _set; | ||
set(options: { | ||
sql?: Fragment | Fragment[]; | ||
rawSql?: string | string[]; | ||
values?: Record<string, any>; | ||
}): this; | ||
get hasSet(): boolean; | ||
buildUpdate(): string; | ||
build(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
export interface ColumnDefinition { | ||
name: string; | ||
alias?: string; | ||
type: string; | ||
primaryKey?: boolean; | ||
autoIncrement?: boolean; | ||
required?: boolean; | ||
precision?: number; | ||
scale?: number; | ||
unique?: boolean; | ||
index?: boolean; | ||
defaultValue?: string; | ||
} | ||
export type Changes = { | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}; | ||
export interface Statement<ReturnType, ParamsType extends DbBinding[]> { | ||
get native(): any; | ||
all(...params: ParamsType): Promise<ReturnType[]>; | ||
one(...params: ParamsType): Promise<ReturnType | null>; | ||
value<ReturnValue>(...params: ParamsType): Promise<ReturnValue | null>; | ||
arrays(...params: ParamsType): Promise<any[][]>; | ||
array(...params: ParamsType): Promise<any[] | null>; | ||
exec(...params: ParamsType): Promise<Changes>; | ||
run(...params: ParamsType): Promise<void>; | ||
} | ||
export interface SyncStatement<ReturnType, ParamsType extends DbBinding[]> { | ||
get native(): any; | ||
as<T extends Constructor<any>>(t: T): SyncStatement<T, ParamsType>; | ||
allSync(...params: ParamsType): ReturnType[]; | ||
oneSync(...params: ParamsType): ReturnType | null; | ||
valueSync<ReturnValue>(...params: ParamsType): ReturnValue | null; | ||
arraysSync(...params: ParamsType): any[][]; | ||
arraySync(...params: ParamsType): any[] | null; | ||
execSync(...params: ParamsType): Changes; | ||
runSync(...params: ParamsType): void; | ||
} | ||
export interface TypeConverter { | ||
toDb(value: any): any; | ||
fromDb(value: any): any; | ||
} | ||
export interface NamingStrategy { | ||
tableName(table: string): string; | ||
columnName(column: string): string; | ||
tableFromDef(def: TableDefinition): string; | ||
} | ||
export interface Dialect { | ||
get $(): any; | ||
strategy: NamingStrategy; | ||
quote(name: string): string; | ||
quoteTable(name: string): string; | ||
quoteColumn(name: string): string; | ||
sqlLimit(skip?: number, take?: number): Fragment; | ||
} | ||
export interface Driver { | ||
get dialect(): Dialect; | ||
get schema(): Schema; | ||
get converters(): { | ||
[key: string]: TypeConverter; | ||
}; | ||
} | ||
export type OnJoin<First extends Constructor<any>, Second extends Constructor<any>, Table extends Constructor<any>> = (from: TypeRef<InstanceType<First>>, to: TypeRef<InstanceType<Second>>, table: TypeRef<InstanceType<Table>>) => Fragment; | ||
declare class SqlJoinBuilder<Tables extends Constructor<any>[]> implements JoinBuilder<First<Tables>> { | ||
export interface Connection { | ||
driver: Driver; | ||
get table(): First<Tables>; | ||
tables: Tables; | ||
refs: ConstructorsToRefs<Tables>; | ||
$: ReturnType<typeof Sql.create>; | ||
/** | ||
* Prepare a parameterized statement and return an async Statement | ||
*/ | ||
prepare<ReturnType, ParamsType extends DbBinding[]>(sql: TemplateStringsArray | string, ...params: DbBinding[]): Statement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
ParamsType | ||
]>; | ||
} | ||
export interface SyncConnection { | ||
driver: Driver; | ||
/** | ||
* Prepare a parameterized statement and return a sync Statement | ||
*/ | ||
prepareSync<ReturnType, ParamsType extends DbBinding[]>(sql: TemplateStringsArray | string, ...params: DbBinding[]): SyncStatement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
ParamsType | ||
]>; | ||
} | ||
export type Fragment = { | ||
sql: string; | ||
params: Record<string, any>; | ||
alias: string; | ||
buildOn?: (refs: ConstructorsToRefs<Tables>, params: Record<string, any>) => string; | ||
constructor(driver: Driver, ...tables: Tables); | ||
on(expr: (...args: ConstructorsToRefs<Tables>) => Fragment): this; | ||
as(alias: string): this; | ||
build(refs: ConstructorsToRefs<Tables>, type: JoinType): { | ||
type: JoinType; | ||
table: string; | ||
as: any; | ||
on: string; | ||
params: Record<string, any>; | ||
}; | ||
export type IntoFragment<T> = Fragment & { | ||
into: T; | ||
}; | ||
export interface SqlBuilder { | ||
build(): Fragment; | ||
} | ||
export type WhereOptions = { | ||
equals?: Record<string, ScalarValue>; | ||
notEquals?: Record<string, ScalarValue>; | ||
like?: Record<string, ScalarValue>; | ||
notLike?: Record<string, ScalarValue>; | ||
startsWith?: Record<string, ScalarValue>; | ||
endsWith?: Record<string, ScalarValue>; | ||
contains?: Record<string, ScalarValue>; | ||
in?: Record<string, ScalarValue[]>; | ||
notIn?: Record<string, ScalarValue[]>; | ||
isNull?: string[]; | ||
notNull?: string[]; | ||
op?: [ | ||
string, | ||
Record<string, any> | ||
]; | ||
sql?: Fragment; | ||
rawSql?: string | string[]; | ||
params?: Record<string, any>; | ||
}; | ||
export type JoinType = "JOIN" | "INNER JOIN" | "LEFT JOIN" | "RIGHT JOIN" | "OUTER JOIN" | "FULL JOIN" | "CROSS JOIN"; | ||
export type JoinDefinition = { | ||
type: JoinType; | ||
on?: string; | ||
params?: Record<string, any>; | ||
}; | ||
export interface JoinBuilder<Table extends Constructor<any>> { | ||
get table(): Table; | ||
get tables(): Constructor<any>[]; | ||
build(refs: ConstructorsToRefs<any>, type: JoinType): JoinDefinition; | ||
} | ||
export interface GroupByBuilder { | ||
build(refs: ConstructorsToRefs<any>): Fragment; | ||
} | ||
export interface HavingBuilder { | ||
build(refs: ConstructorsToRefs<any>): Fragment; | ||
} | ||
export interface OrderByBuilder { | ||
build(refs: ConstructorsToRefs<any>): Fragment; | ||
} | ||
export declare class Meta { | ||
cls: ReflectMeta; | ||
static metadata: { | ||
[id: symbol]: Meta; | ||
}; | ||
constructor(cls: ReflectMeta); | ||
static assertClass(table: ClassParam): ReflectMeta; | ||
static assertTable(table: ClassParam): ReflectMeta; | ||
static assertMeta(table: ClassParam): Meta; | ||
get name(): string; | ||
get tableName(): string; | ||
get type(): { | ||
name: string; | ||
table?: TableDefinition | undefined; | ||
}; | ||
get table(): TableDefinition; | ||
get props(): [ | ||
{ | ||
name: string; | ||
column?: ColumnDefinition | undefined; | ||
} | ||
]; | ||
get columns(): ColumnDefinition[]; | ||
} | ||
export type OnJoin<First extends Constructor<any>, Second extends Constructor<any>, Table extends Constructor<any>> = (from: TypeRef<InstanceType<First>>, to: TypeRef<InstanceType<Second>>, table: TypeRef<InstanceType<Table>>) => Fragment; | ||
export type QueryType<T> = T extends SelectQuery<any> ? SelectQuery<any> : T extends UpdateQuery<any> ? UpdateQuery<any> : T extends DeleteQuery<any> ? DeleteQuery<any> : WhereQuery<any>; | ||
export type This<T, NewTables extends Constructor<any>[]> = QueryType<T> extends SelectQuery<any> ? SelectQuery<NewTables> : QueryType<T> extends UpdateQuery<any> ? UpdateQuery<NewTables> : QueryType<T> extends DeleteQuery<any> ? DeleteQuery<NewTables> : WhereQuery<NewTables>; | ||
export declare class WhereQuery<Tables extends Constructor<any>[]> { | ||
driver: Driver; | ||
export declare class WhereQuery<Tables extends Constructor<any>[]> implements SqlBuilder { | ||
$: ReturnType<typeof Sql.create>; | ||
tables: [ | ||
@@ -83,6 +252,5 @@ ...Tables | ||
refs: TypeRefs<Tables>; | ||
constructor(driver: Driver, tables: [ | ||
constructor($: ReturnType<typeof Sql.create>, tables: [ | ||
...Tables | ||
], metas: Meta[], refs: TypeRefs<Tables>); | ||
$: ReturnType<typeof Sql.create>; | ||
protected _where: { | ||
@@ -103,4 +271,3 @@ condition: string; | ||
}; | ||
prevJoin(): TypeRef<InstanceType<Constructor<any>>>; | ||
createInstance<NewTable extends Constructor<any>>(table: NewTable): This<typeof WhereQuery, [ | ||
protected createInstance<NewTable extends Constructor<any>>(table: NewTable, ref?: TypeRef<InstanceType<NewTable>>): This<typeof WhereQuery, [ | ||
...Tables, | ||
@@ -110,64 +277,67 @@ NewTable | ||
copyInto(instance: WhereQuery<any>): WhereQuery<any>; | ||
addJoin<NewTable extends Constructor<any>>(join: { | ||
clone(): WhereQuery<Tables>; | ||
protected addJoin<NewTable extends Constructor<any>>(options: { | ||
type: JoinType; | ||
cls: NewTable; | ||
ref?: TypeRef<InstanceType<NewTable>>; | ||
on?: string | ((...params: any[]) => Fragment); | ||
as?: string; | ||
params?: Record<string, any>; | ||
}): This<this, [ | ||
}): This<typeof WhereQuery, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
joinBuilder<NewTable extends Constructor<any>>(builder: JoinBuilder<NewTable>, typeHint?: JoinType): This<this, [ | ||
protected joinBuilder<NewTable extends Constructor<any>>(builder: JoinBuilder<NewTable>, typeHint?: JoinType): This<typeof WhereQuery, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
join<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable>, options?: { | ||
join<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable> | TypeRef<InstanceType<NewTable>>, options?: { | ||
on?: OnJoin<Last<Tables>, NewTable, First<Tables>>; | ||
as?: string; | ||
} | SqlBuilder): This<this, [ | ||
} | SqlBuilder | Fragment): This<this, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
leftJoin<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable>, options?: { | ||
leftJoin<NewTable extends Constructor<any>>(cls: NewTable | TypeRef<InstanceType<NewTable>>, options?: { | ||
on?: OnJoin<Last<Tables>, NewTable, First<Tables>>; | ||
as?: string; | ||
} | SqlBuilder): This<this, [ | ||
} | SqlBuilder | Fragment): This<this, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
rightJoin<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable>, options?: { | ||
rightJoin<NewTable extends Constructor<any>>(cls: NewTable | TypeRef<InstanceType<NewTable>>, options?: { | ||
on?: OnJoin<Last<Tables>, NewTable, First<Tables>>; | ||
as?: string; | ||
} | SqlBuilder): This<this, [ | ||
} | SqlBuilder | Fragment): This<this, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
fullJoin<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable>, options?: { | ||
fullJoin<NewTable extends Constructor<any>>(cls: NewTable | TypeRef<InstanceType<NewTable>>, options?: { | ||
on?: OnJoin<Last<Tables>, NewTable, First<Tables>>; | ||
as?: string; | ||
} | SqlBuilder): This<this, [ | ||
} | SqlBuilder | Fragment): This<this, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
crossJoin<NewTable extends Constructor<any>>(cls: NewTable | JoinBuilder<NewTable>, options?: { | ||
crossJoin<NewTable extends Constructor<any>>(cls: NewTable | TypeRef<InstanceType<NewTable>>, options?: { | ||
on?: OnJoin<Last<Tables>, NewTable, First<Tables>>; | ||
as?: string; | ||
} | SqlBuilder): This<this, [ | ||
} | SqlBuilder | Fragment): This<this, [ | ||
...Tables, | ||
NewTable | ||
]>; | ||
where(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
and(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
or(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
condition(condition: "AND" | "OR", options: WhereOptions): this; | ||
where(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment) | Fragment, ...params: any[]): this; | ||
and(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment) | Fragment, ...params: any[]): this; | ||
or(options: WhereOptions | TemplateStringsArray | ((...params: TypeRefs<Tables>) => Fragment) | Fragment, ...params: any[]): this; | ||
condition(condition: "AND" | "OR", options: WhereOptions | Fragment): this; | ||
quote(symbol: string): string; | ||
quoteTable(table: string): string; | ||
quoteColumn(column: string): string; | ||
alias(alias?: string): this; | ||
as(alias?: string): this; | ||
protected addParams(params?: Record<string, any>): void; | ||
protected mergeParams(f: Fragment): string; | ||
private addWhere; | ||
buildWhere(): string; | ||
buildJoins(): string; | ||
protected buildWhere(): string; | ||
protected buildJoins(): string; | ||
into<T extends Constructor<any>>(into: T): IntoFragment<InstanceType<T>>; | ||
build(): { | ||
@@ -178,2 +348,57 @@ sql: string; | ||
} | ||
export type SelectOptions = { | ||
props?: string[]; | ||
columns?: string[]; | ||
sql?: Fragment; | ||
}; | ||
export declare class SelectQuery<Tables extends Constructor<any>[]> extends WhereQuery<Tables> { | ||
protected _select: string[]; | ||
protected _groupBy: string[]; | ||
protected _having: string[]; | ||
protected _orderBy: string[]; | ||
protected _skip: number | undefined; | ||
protected _take: number | undefined; | ||
protected _limit?: string; | ||
copyInto(instance: SelectQuery<any>): SelectQuery<any>; | ||
clone(): SelectQuery<Tables>; | ||
groupBy(options: TemplateStringsArray | Fragment | GroupByBuilder | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
having(options: TemplateStringsArray | Fragment | HavingBuilder | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
orderBy(options: TemplateStringsArray | Fragment | OrderByBuilder | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
select(options: SelectOptions | TemplateStringsArray | string | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
get hasSelect(): boolean; | ||
skip(rows?: number): this; | ||
take(rows?: number): this; | ||
limit(take?: number, skip?: number): this; | ||
exists(): IntoFragment<Boolean>; | ||
rowCount(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
into: NumberConstructor; | ||
}; | ||
protected buildSelect(): string; | ||
protected buildFrom(): string; | ||
protected buildGroupBy(): string; | ||
protected buildHaving(): string; | ||
protected buildOrderBy(): string; | ||
protected buildLimit(): string; | ||
build(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
into: [ | ||
...Tables | ||
][0] | undefined; | ||
}; | ||
} | ||
export declare class UpdateQuery<Tables extends Constructor<any>[]> extends WhereQuery<Tables> { | ||
private _set; | ||
set(options: TemplateStringsArray | Fragment | { | ||
[K in keyof Partial<InstanceType<First<Tables>>>]: any; | ||
} | ((...params: TypeRefs<Tables>) => Fragment), ...params: any[]): this; | ||
get hasSet(): boolean; | ||
buildUpdate(): string; | ||
build(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
}; | ||
} | ||
export declare class DeleteQuery<Tables extends Constructor<any>[]> extends WhereQuery<Tables> { | ||
@@ -186,20 +411,82 @@ buildDelete(): string; | ||
} | ||
declare class Meta { | ||
cls: ReflectMeta; | ||
constructor(cls: ReflectMeta); | ||
get name(): string; | ||
get tableName(): string; | ||
get type(): { | ||
name: string; | ||
table?: TableDefinition | undefined; | ||
export declare class Sql { | ||
static ops: { | ||
[key: string]: string; | ||
}; | ||
get table(): TableDefinition; | ||
get props(): [ | ||
{ | ||
name: string; | ||
column?: ColumnDefinition | undefined; | ||
} | ||
]; | ||
get columns(): ColumnDefinition[]; | ||
static opKeys: string[]; | ||
static create(dialect: Dialect): { | ||
(strings: TemplateStringsArray | string, ...params: any[]): Fragment; | ||
dialect: Dialect; | ||
quote: (name: string) => string; | ||
quoteColumn: (name: string) => string; | ||
quoteTable: (name: string) => string; | ||
ref<Table extends Constructor<any>>(cls: Table, as?: string): TypeRef<InstanceType<Table>>; | ||
refs<T extends readonly Constructor[]>(...classes_0: T): ConstructorToTypeRef<T>; | ||
fragment(sql: string | Fragment, params?: Record<string, any>): Fragment; | ||
from<Table_1 extends Constructor<any>>(table: Table_1 | TypeRef<InstanceType<Table_1>>, alias?: string): SelectQuery<[ | ||
Table_1 | ||
]>; | ||
update<Table_2 extends Constructor<any>>(table: Table_2): UpdateQuery<[ | ||
Table_2 | ||
]>; | ||
deleteFrom<Table_3 extends Constructor<any>>(table: Table_3): DeleteQuery<[ | ||
Table_3 | ||
]>; | ||
join<Tables extends Constructor<any>[]>(...tables: Tables): SqlJoinBuilder<Tables>; | ||
groupBy<Tables_1 extends Constructor<any>[]>(...tables: Tables_1): SqlGroupByBuilder<Tables_1>; | ||
having<Tables_2 extends Constructor<any>[]>(...tables: Tables_2): SqlHavingBuilder<Tables_2>; | ||
orderBy<Tables_3 extends Constructor<any>[]>(...tables: Tables_3): SqlOrderByBuilder<Tables_3>; | ||
idEquals<Table_4 extends { | ||
id: number | string; | ||
}>(id: number | string): (x: Table_4) => Fragment; | ||
log(obj: any): void; | ||
dump(obj: any[]): void; | ||
}; | ||
} | ||
declare class SqlJoinBuilder<Tables extends Constructor<any>[]> implements JoinBuilder<First<Tables>> { | ||
$: ReturnType<typeof Sql.create>; | ||
get table(): First<Tables>; | ||
tables: Tables; | ||
refs: ConstructorsToRefs<Tables>; | ||
exprs: { | ||
type: JoinType; | ||
expr: ((refs: ConstructorsToRefs<Tables>) => Fragment); | ||
}[]; | ||
params: Record<string, any>; | ||
alias: string; | ||
buildOn?: (refs: ConstructorsToRefs<Tables>, params: Record<string, any>) => string; | ||
constructor($: ReturnType<typeof Sql.create>, ...tables: Tables); | ||
join(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
leftJoin(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
rightJoin(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
fullJoin(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
crossJoin(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
add(type: JoinType, expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
as(alias: string): this; | ||
build(refs: ConstructorsToRefs<Tables>): { | ||
type: JoinType; | ||
on: string; | ||
params: Record<string, any>; | ||
}; | ||
} | ||
declare class SqlBuilderBase<Tables extends Constructor<any>[]> { | ||
$: ReturnType<typeof Sql.create>; | ||
tables: Tables; | ||
params: Record<string, any>; | ||
exprs: ((refs: ConstructorsToRefs<Tables>) => Fragment)[]; | ||
delimiter: string; | ||
constructor($: ReturnType<typeof Sql.create>, ...tables: Tables); | ||
add(expr: ((...args: ConstructorsToRefs<Tables>) => Fragment) | TemplateStringsArray, ...params: any[]): this; | ||
build(refs: ConstructorsToRefs<Tables>): { | ||
sql: string; | ||
params: Record<string, any>; | ||
}; | ||
} | ||
declare class SqlGroupByBuilder<Tables extends Constructor<any>[]> extends SqlBuilderBase<Tables> implements GroupByBuilder { | ||
} | ||
declare class SqlOrderByBuilder<Tables extends Constructor<any>[]> extends SqlBuilderBase<Tables> implements OrderByBuilder { | ||
} | ||
declare class SqlHavingBuilder<Tables extends Constructor<any>[]> extends SqlBuilderBase<Tables> implements HavingBuilder { | ||
constructor($: ReturnType<typeof Sql.create>, ...tables: Tables); | ||
} | ||
export type InsertOptions = { | ||
@@ -216,248 +503,100 @@ /** only insert these props */ | ||
onlyWithValues?: boolean; | ||
/** force update even with no where clause */ | ||
force?: boolean; | ||
}; | ||
export type DeleteOptions = { | ||
/** force delete even with no where clause */ | ||
force?: boolean; | ||
type DeleteOptions$1 = { | ||
where?: Fragment | Fragment[]; | ||
}; | ||
export declare class ConnectionBase { | ||
export declare class DbConnection { | ||
connection: Connection & { | ||
$: ReturnType<typeof Sql.create>; | ||
}; | ||
driver: Driver; | ||
$: ReturnType<typeof Sql.create>; | ||
constructor(driver: Driver); | ||
schema: Schema; | ||
constructor(connection: Connection & { | ||
$: ReturnType<typeof Sql.create>; | ||
}); | ||
get sync(): SyncDbConnection; | ||
quote(symbol: string): string; | ||
from<Table extends Constructor<any>>(table: Table): SelectQuery<[ | ||
Table | ||
]>; | ||
updateFor<Table extends Constructor<any>>(table: Table): UpdateQuery<[ | ||
Table | ||
]>; | ||
deleteFrom<Table extends Constructor<any>>(table: Table): DeleteQuery<[ | ||
Table | ||
]>; | ||
} | ||
export declare class Connection extends ConnectionBase { | ||
get sync(): SyncConnection; | ||
quote(symbol: string): string; | ||
insert<T extends ClassInstance>(row: T, options?: InsertOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
} | undefined>; | ||
insertAll<T extends ClassInstance>(rows: T[], options?: InsertOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
} | null | undefined>; | ||
listTables(): Promise<unknown[]>; | ||
dropTable<Table extends ClassParam>(table: Table): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
createTable<Table extends ClassParam>(table: Table): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
insert<T extends ClassInstance>(row: T, options?: InsertOptions): Promise<Changes>; | ||
insertAll<T extends ClassInstance>(rows: T[], options?: InsertOptions): Promise<Changes>; | ||
listTables(): Promise<string[]>; | ||
dropTable<Table extends ClassParam>(table: Table): Promise<Changes>; | ||
createTable<Table extends ClassParam>(table: Table): Promise<Changes>; | ||
all<ReturnType>(strings: TemplateStringsArray, ...params: any[]): Promise<ReturnType[]>; | ||
single<ReturnType>(strings: TemplateStringsArray, ...params: any[]): Promise<ReturnType | null>; | ||
} | ||
export declare class SyncConnection extends ConnectionBase { | ||
insert<T extends ClassInstance>(row: T, options?: InsertOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}> | undefined; | ||
insertAll<T extends ClassInstance>(rows: T[], options?: InsertOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}> | null | undefined; | ||
update<T extends ClassInstance>(row: T, options?: UpdateOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}> | undefined; | ||
delete<T extends ClassInstance>(row: T, options?: DeleteOptions): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}> | undefined; | ||
listTables(): unknown[]; | ||
dropTable<Table extends ClassParam>(table: Table): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
createTable<Table extends ClassParam>(table: Table): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
createStatment<T>(strings: TemplateStringsArray | SqlBuilder, ...params: any[]): [ | ||
one<ReturnType>(strings: TemplateStringsArray, ...params: any[]): Promise<Awaited<ReturnType> | null>; | ||
column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<ReturnValue[]>; | ||
value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<unknown>; | ||
arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<any[][]>; | ||
array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<any[] | null>; | ||
exec(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<Changes>; | ||
run(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Promise<void>; | ||
prepare<T>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): [ | ||
Statement<T, DbBinding[]> | Statement<T, any>, | ||
any[] | Record<string, any> | ||
]; | ||
all<ReturnType>(strings: TemplateStringsArray | SqlBuilder, ...params: any[]): ReturnType[]; | ||
single<ReturnType>(strings: TemplateStringsArray | SqlBuilder, ...params: any[]): ReturnType | null; | ||
column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder, ...params: any[]): unknown[]; | ||
scalar<ReturnValue>(strings: TemplateStringsArray | SqlBuilder, ...params: any[]): unknown; | ||
exec(sql: string | SqlBuilder, params: Record<string, any>): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
} | ||
export declare class NamingStrategy { | ||
tableName(table: string): string; | ||
columnName(column: string): string; | ||
tableFromDef(def: TableDefinition): string; | ||
} | ||
export type ConstructorsToRefs<T extends Constructor<any>[]> = { | ||
[K in keyof T]: TypeRef<InstanceType<T[K]>>; | ||
}; | ||
export type First<T extends readonly any[]> = T extends [ | ||
infer F, | ||
...any[] | ||
] ? F extends Constructor<any> ? F : never : never; | ||
export type Last<T extends readonly any[]> = T extends [ | ||
...any[], | ||
infer L | ||
] ? L extends Constructor<any> ? L : never : never; | ||
export type ScalarValue = string | bigint | number | boolean | null | symbol; | ||
export type DbBinding = string | bigint | NodeJS.TypedArray | number | boolean | null | Record<string, string | bigint | NodeJS.TypedArray | number | boolean | null>; | ||
export interface ReflectMeta { | ||
name: string; | ||
$id: symbol; | ||
$type: { | ||
name: string; | ||
table?: TableDefinition; | ||
export declare class SyncDbConnection { | ||
connection: SyncConnection & { | ||
$: ReturnType<typeof Sql.create>; | ||
}; | ||
$props: [ | ||
{ | ||
name: string; | ||
column?: ColumnDefinition; | ||
} | ||
driver: Driver; | ||
$: ReturnType<typeof Sql.create>; | ||
schema: Schema; | ||
constructor(connection: SyncConnection & { | ||
$: ReturnType<typeof Sql.create>; | ||
}); | ||
quote(symbol: string): string; | ||
insert<T extends ClassInstance>(row: T, options?: InsertOptions): Changes; | ||
insertAll<T extends ClassInstance>(rows: T[], options?: InsertOptions): Changes; | ||
update<T extends ClassInstance>(row: T, options?: UpdateOptions): Changes; | ||
delete<T extends ClassInstance>(row: T, options?: DeleteOptions$1): Changes; | ||
listTables(): string[]; | ||
dropTable<Table extends ClassParam>(table: Table): Changes; | ||
createTable<Table extends ClassParam>(table: Table): Changes; | ||
prepareSync<T>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): [ | ||
SyncStatement<T, DbBinding[]> | SyncStatement<T, any>, | ||
any[] | Record<string, any>, | ||
T | undefined | ||
]; | ||
all<ReturnType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<ReturnType>, ...params: any[]): ReturnType[]; | ||
one<ReturnType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<ReturnType>, ...params: any[]): ReturnType | null; | ||
column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue[]; | ||
value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): unknown; | ||
arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]; | ||
array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[] | null; | ||
exec(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Changes; | ||
run(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): void; | ||
} | ||
export type ColumnType = "INTEGER" | "SMALLINT" | "BIGINT" | "DECIMAL" | "NUMERIC" | "REAL" | "FLOAT" | "DOUBLE" | "MONEY" | "DATE" | "DATETIME" | "TIME" | "TIMEZ" | "TIMESTAMP" | "TIMESTAMPZ" | "INTERVAL" | "BOOLEAN" | "UUID" | "BLOB" | "BYTES" | "BIT" | "TEXT" | "VARCHAR" | "NVARCHAR" | "CHAR" | "NCHAR" | "JSON" | "JSONB" | "XML"; | ||
export type Constructor<T = any> = new (...args: any[]) => T; | ||
export type ClassParam = ReflectMeta | { | ||
constructor: ReflectMeta; | ||
} | Constructor<any>; | ||
export type ClassInstance = { | ||
constructor: ReflectMeta; | ||
} & Record<string, any> | Record<string, any>; | ||
export interface TableDefinition { | ||
name: string; | ||
alias?: string; | ||
} | ||
export interface ColumnDefinition { | ||
name: string; | ||
alias?: string; | ||
type: string; | ||
primaryKey?: boolean; | ||
autoIncrement?: boolean; | ||
required?: boolean; | ||
precision?: number; | ||
scale?: number; | ||
unique?: boolean; | ||
index?: boolean; | ||
defaultValue?: string; | ||
} | ||
export interface Statement<ReturnType, ParamsType extends DbBinding[]> { | ||
get native(): any; | ||
all(...params: ParamsType): Promise<ReturnType[]>; | ||
allSync(...params: ParamsType): ReturnType[]; | ||
first(...params: ParamsType): Promise<ReturnType | null>; | ||
firstSync(...params: ParamsType): ReturnType | null; | ||
column<ReturnValue>(...params: ParamsType): Promise<ReturnValue[]>; | ||
columnSync<ReturnValue>(...params: ParamsType): ReturnValue[]; | ||
scalar<ReturnValue>(...params: ParamsType): Promise<ReturnValue | null>; | ||
scalarSync<ReturnValue>(...params: ParamsType): ReturnValue | null; | ||
arrays(...params: ParamsType): Promise<any[][]>; | ||
arraysSync(...params: ParamsType): any[][]; | ||
array(...params: ParamsType): Promise<any[] | null>; | ||
arraySync(...params: ParamsType): any[] | null; | ||
exec(...params: ParamsType): Promise<{ | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
}>; | ||
execSync(...params: ParamsType): { | ||
changes: number; | ||
lastInsertRowid: number | bigint; | ||
declare class ConnectionBase { | ||
driver: Driver & { | ||
$: ReturnType<typeof Sql.create>; | ||
}; | ||
} | ||
export interface TypeConverter { | ||
toDb(value: any): any; | ||
fromDb(value: any): any; | ||
} | ||
export interface Driver { | ||
get $(): any; | ||
get name(): string; | ||
get async(): Connection; | ||
get sync(): SyncConnection | undefined; | ||
get converters(): { | ||
[key: string]: TypeConverter; | ||
}; | ||
quote(name: string): string; | ||
quoteTable(name: string): string; | ||
quoteColumn(name: string): string; | ||
sqlColumnDefinition(column: ColumnDefinition): string; | ||
sqlIndexDefinition(table: TableDefinition, column: ColumnDefinition): string; | ||
sqlTableNames(schema?: string): string; | ||
sqlLimit(skip?: number, take?: number): string; | ||
prepareRaw<ReturnType, ParamsType extends DbBinding[]>(sql: String): Statement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
$: ReturnType<typeof Sql.create>; | ||
async: DbConnection; | ||
sync: SyncDbConnection; | ||
schema: Schema; | ||
dialect: Dialect; | ||
constructor(driver: Driver & { | ||
$: ReturnType<typeof Sql.create>; | ||
}); | ||
prepare<ReturnType, ParamsType extends DbBinding[]>(sql: TemplateStringsArray | string, ...params: DbBinding[]): Statement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
ParamsType | ||
]>; | ||
prepare<ReturnType, ParamsType extends DbBinding[]>(strings: TemplateStringsArray, ...params: DbBinding[]): Statement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
prepareSync<ReturnType, ParamsType extends DbBinding[]>(sql: TemplateStringsArray | string, ...params: DbBinding[]): SyncStatement<ReturnType, ParamsType extends any[] ? ParamsType : [ | ||
ParamsType | ||
]>; | ||
} | ||
export type Fragment = { | ||
sql: string; | ||
params?: Record<string, any>; | ||
}; | ||
export interface SqlBuilder { | ||
build(): { | ||
sql: string; | ||
params: Record<string, any>; | ||
}; | ||
export declare class DefaultStrategy implements NamingStrategy { | ||
tableName(table: string): string; | ||
columnName(column: string): string; | ||
tableFromDef(def: TableDefinition): string; | ||
} | ||
export type WhereOptions = { | ||
equals?: Record<string, ScalarValue>; | ||
notEquals?: Record<string, ScalarValue>; | ||
like?: Record<string, ScalarValue>; | ||
notLike?: Record<string, ScalarValue>; | ||
startsWith?: Record<string, ScalarValue>; | ||
endsWith?: Record<string, ScalarValue>; | ||
contains?: Record<string, ScalarValue>; | ||
in?: Record<string, ScalarValue[]>; | ||
notIn?: Record<string, ScalarValue[]>; | ||
isNull?: string[]; | ||
notNull?: string[]; | ||
op?: [ | ||
string, | ||
Record<string, any> | ||
]; | ||
sql?: Fragment | Fragment[]; | ||
rawSql?: string | string[]; | ||
params?: Record<string, any>; | ||
export declare class SnakeCaseStrategy implements NamingStrategy { | ||
tableName(table: string): string; | ||
columnName(column: string): string; | ||
tableFromDef(def: TableDefinition): string; | ||
} | ||
export declare function useFilter(db: SyncDbConnection, filter: (sql: TemplateStringsArray | string, params: DbBinding[]) => void): { | ||
release: () => void; | ||
}; | ||
export type TypeRef<T> = T & { | ||
$ref: { | ||
cls: Constructor<T>; | ||
as?: string; | ||
}; | ||
}; | ||
export type ConstructorToTypeRef<T extends readonly any[]> = { | ||
[K in keyof T]: T[K] extends new (...args: any[]) => infer R ? TypeRef<R> : never; | ||
}; | ||
export type JoinType = "JOIN" | "INNER JOIN" | "LEFT JOIN" | "RIGHT JOIN" | "OUTER JOIN" | "FULL JOIN" | "CROSS JOIN"; | ||
export type TypeRefs<Tables extends Constructor<any>[]> = { | ||
[K in keyof Tables]: TypeRef<InstanceType<Tables[K]>>; | ||
}; | ||
export type JoinDefinition = { | ||
type: JoinType; | ||
table: string; | ||
on?: string; | ||
as?: string; | ||
params?: Record<string, any>; | ||
}; | ||
export interface JoinBuilder<Table extends Constructor<any>> { | ||
get table(): Table; | ||
get tables(): Constructor<any>[]; | ||
build(refs: ConstructorsToRefs<any>, type: JoinType): JoinDefinition; | ||
} | ||
export declare class Inspect { | ||
@@ -469,2 +608,9 @@ static dump(obj: any): string; | ||
} | ||
export declare function pick<T extends Record<string, any> | Record<string, any>[]>(input: T, keys: string[]): T extends Record<string, any>[] ? Record<string, any>[] : Record<string, any>; | ||
export declare function omit<T extends Record<string, any> | Record<string, any>[]>(input: T, keys: string[]): T extends Record<string, any>[] ? Record<string, any>[] : Record<string, any>; | ||
export declare function toStr(value: any): string; | ||
export declare function nextParam(params: Record<string, any>): string; | ||
export declare function mergeParams(params: Record<string, any>, f: Fragment): string; | ||
export declare function isTemplateStrings(arg: any): arg is TemplateStringsArray; | ||
export declare function snakeCase(s: string): string; | ||
export declare function converterFor(converter: TypeConverter, ...dataTypes: string[]): { | ||
@@ -492,6 +638,6 @@ [key: string]: TypeConverter; | ||
}): (target: any, propertyKey: string) => void; | ||
export declare function Table<T extends Constructor<any>>(cls: new () => T, definition: TableDefinition$1<T>): new () => T; | ||
interface TableDefinition$1<T> { | ||
export declare function Table<T extends Constructor<any>>(cls: T, definition: TableDefinition$1<T>): T; | ||
interface TableDefinition$1<T extends Constructor<any>> { | ||
table?: TableConfig; | ||
columns: ColumnsConfig<T>; | ||
columns: ColumnsConfig<InstanceType<T>>; | ||
} | ||
@@ -554,3 +700,177 @@ export interface TableConfig { | ||
}; | ||
export declare class Sqlite implements Driver { | ||
static connection: ConnectionBase; | ||
static driver: Driver; | ||
static schema: Schema; | ||
static init(): SqliteConnection; | ||
name: string; | ||
dialect: Dialect; | ||
schema: Schema; | ||
strategy: NamingStrategy; | ||
$: ReturnType<typeof Sql.create>; | ||
variables: { | ||
[key: string]: string; | ||
}; | ||
types: DialectTypes; | ||
converters: { | ||
[key: string]: TypeConverter; | ||
}; | ||
constructor(); | ||
} | ||
declare class SqliteConnection extends ConnectionBase { | ||
} | ||
export declare class SqliteDialect implements Dialect { | ||
$: ReturnType<typeof Sql.create>; | ||
strategy: DefaultStrategy; | ||
constructor(); | ||
quote(name: string): string; | ||
quoteTable(name: string): string; | ||
quoteColumn(name: string): string; | ||
sqlLimit(offset?: number, limit?: number): Fragment; | ||
} | ||
export declare class MySql extends Sqlite { | ||
static connection: ConnectionBase; | ||
static driver: Driver; | ||
static schema: Schema; | ||
static init(): MySqlConnection; | ||
constructor(); | ||
} | ||
declare class MySqlConnection extends ConnectionBase { | ||
} | ||
export declare class MySqlDialect implements Dialect { | ||
$: ReturnType<typeof Sql.create>; | ||
strategy: DefaultStrategy; | ||
constructor(); | ||
quote(name: string): string; | ||
quoteTable(name: string): string; | ||
quoteColumn(name: string): string; | ||
sqlLimit(offset?: number, limit?: number): Fragment; | ||
} | ||
export declare class PostgreSql extends Sqlite { | ||
static connection: ConnectionBase; | ||
static driver: Driver; | ||
static schema: Schema; | ||
static init(): PostgreSqlConnection; | ||
constructor(); | ||
} | ||
declare class PostgreSqlConnection extends ConnectionBase { | ||
} | ||
export declare class PostgreSqlDialect implements Dialect { | ||
$: ReturnType<typeof Sql.create>; | ||
strategy: DefaultStrategy; | ||
constructor(); | ||
quote(name: string): string; | ||
quoteTable(name: string): string; | ||
quoteColumn(name: string): string; | ||
sqlLimit(offset?: number, limit?: number): Fragment; | ||
} | ||
export declare class SqliteSchema extends Schema { | ||
driver: Driver & { | ||
$: ReturnType<typeof Sql.create>; | ||
types: DialectTypes; | ||
variables: { | ||
[key: string]: string; | ||
}; | ||
}; | ||
constructor(driver: Driver & { | ||
$: ReturnType<typeof Sql.create>; | ||
types: DialectTypes; | ||
variables: { | ||
[key: string]: string; | ||
}; | ||
}); | ||
sqlTableNames(): string; | ||
sqlIndexDefinition(table: TableDefinition, column: ColumnDefinition): string; | ||
sqlColumnDefinition(column: ColumnDefinition): string; | ||
sqlLimit(offset?: number, limit?: number): Fragment; | ||
} | ||
export declare class MySqlSchema extends SqliteSchema { | ||
} | ||
export declare class PostgreSqlSchema extends SqliteSchema { | ||
} | ||
export declare const sqlite: { | ||
(strings: string | TemplateStringsArray, ...params: any[]): Fragment; | ||
dialect: Dialect; | ||
quote: (name: string) => string; | ||
quoteColumn: (name: string) => string; | ||
quoteTable: (name: string) => string; | ||
ref<Table extends Constructor<any>>(cls: Table, as?: string | undefined): TypeRef<InstanceType<Table>>; | ||
refs<T extends readonly Constructor[]>(...classes_0: T): ConstructorToTypeRef<T>; | ||
fragment(sql: string | Fragment, params?: Record<string, any>): Fragment; | ||
from<Table_1 extends Constructor<any>>(table: Table_1 | TypeRef<InstanceType<Table_1>>, alias?: string | undefined): SelectQuery<[ | ||
Table_1 | ||
]>; | ||
update<Table_2 extends Constructor<any>>(table: Table_2): UpdateQuery<[ | ||
Table_2 | ||
]>; | ||
deleteFrom<Table_3 extends Constructor<any>>(table: Table_3): DeleteQuery<[ | ||
Table_3 | ||
]>; | ||
join<Tables extends Constructor<any>[]>(...tables: Tables): SqlJoinBuilder<Tables>; | ||
groupBy<Tables_1 extends Constructor<any>[]>(...tables: Tables_1): SqlGroupByBuilder<Tables_1>; | ||
having<Tables_2 extends Constructor<any>[]>(...tables: Tables_2): SqlHavingBuilder<Tables_2>; | ||
orderBy<Tables_3 extends Constructor<any>[]>(...tables: Tables_3): SqlOrderByBuilder<Tables_3>; | ||
idEquals<Table_4 extends { | ||
id: string | number; | ||
}>(id: string | number): (x: Table_4) => Fragment; | ||
log(obj: any): void; | ||
dump(obj: any[]): void; | ||
}; | ||
export declare const mysql: { | ||
(strings: string | TemplateStringsArray, ...params: any[]): Fragment; | ||
dialect: Dialect; | ||
quote: (name: string) => string; | ||
quoteColumn: (name: string) => string; | ||
quoteTable: (name: string) => string; | ||
ref<Table extends Constructor<any>>(cls: Table, as?: string | undefined): TypeRef<InstanceType<Table>>; | ||
refs<T extends readonly Constructor[]>(...classes_0: T): ConstructorToTypeRef<T>; | ||
fragment(sql: string | Fragment, params?: Record<string, any>): Fragment; | ||
from<Table_1 extends Constructor<any>>(table: Table_1 | TypeRef<InstanceType<Table_1>>, alias?: string | undefined): SelectQuery<[ | ||
Table_1 | ||
]>; | ||
update<Table_2 extends Constructor<any>>(table: Table_2): UpdateQuery<[ | ||
Table_2 | ||
]>; | ||
deleteFrom<Table_3 extends Constructor<any>>(table: Table_3): DeleteQuery<[ | ||
Table_3 | ||
]>; | ||
join<Tables extends Constructor<any>[]>(...tables: Tables): SqlJoinBuilder<Tables>; | ||
groupBy<Tables_1 extends Constructor<any>[]>(...tables: Tables_1): SqlGroupByBuilder<Tables_1>; | ||
having<Tables_2 extends Constructor<any>[]>(...tables: Tables_2): SqlHavingBuilder<Tables_2>; | ||
orderBy<Tables_3 extends Constructor<any>[]>(...tables: Tables_3): SqlOrderByBuilder<Tables_3>; | ||
idEquals<Table_4 extends { | ||
id: string | number; | ||
}>(id: string | number): (x: Table_4) => Fragment; | ||
log(obj: any): void; | ||
dump(obj: any[]): void; | ||
}; | ||
export declare const postgres: { | ||
(strings: string | TemplateStringsArray, ...params: any[]): Fragment; | ||
dialect: Dialect; | ||
quote: (name: string) => string; | ||
quoteColumn: (name: string) => string; | ||
quoteTable: (name: string) => string; | ||
ref<Table extends Constructor<any>>(cls: Table, as?: string | undefined): TypeRef<InstanceType<Table>>; | ||
refs<T extends readonly Constructor[]>(...classes_0: T): ConstructorToTypeRef<T>; | ||
fragment(sql: string | Fragment, params?: Record<string, any>): Fragment; | ||
from<Table_1 extends Constructor<any>>(table: Table_1 | TypeRef<InstanceType<Table_1>>, alias?: string | undefined): SelectQuery<[ | ||
Table_1 | ||
]>; | ||
update<Table_2 extends Constructor<any>>(table: Table_2): UpdateQuery<[ | ||
Table_2 | ||
]>; | ||
deleteFrom<Table_3 extends Constructor<any>>(table: Table_3): DeleteQuery<[ | ||
Table_3 | ||
]>; | ||
join<Tables extends Constructor<any>[]>(...tables: Tables): SqlJoinBuilder<Tables>; | ||
groupBy<Tables_1 extends Constructor<any>[]>(...tables: Tables_1): SqlGroupByBuilder<Tables_1>; | ||
having<Tables_2 extends Constructor<any>[]>(...tables: Tables_2): SqlHavingBuilder<Tables_2>; | ||
orderBy<Tables_3 extends Constructor<any>[]>(...tables: Tables_3): SqlOrderByBuilder<Tables_3>; | ||
idEquals<Table_4 extends { | ||
id: string | number; | ||
}>(id: string | number): (x: Table_4) => Fragment; | ||
log(obj: any): void; | ||
dump(obj: any[]): void; | ||
}; | ||
export {}; |
2095
dist/index.js
@@ -1,2 +0,714 @@ | ||
// src/query.ts | ||
// src/utils.ts | ||
function isDate(d) { | ||
return d && Object.prototype.toString.call(d) === "[object Date]" && !isNaN(d); | ||
} | ||
function toDate(s) { | ||
return !s ? null : isDate(s) ? s : s[0] == "/" ? new Date(parseFloat(/Date\(([^)]+)\)/.exec(s)[1])) : new Date(s); | ||
} | ||
function propsWithValues(obj) { | ||
return Object.keys(obj).filter((k) => obj[k] != null); | ||
} | ||
function uniqueKeys(rows) { | ||
let to = []; | ||
rows.forEach((o) => Object.keys(o).forEach((k) => { | ||
if (to.indexOf(k) === -1) { | ||
to.push(k); | ||
} | ||
})); | ||
return to; | ||
} | ||
function pick(input, keys) { | ||
if (Array.isArray(input)) { | ||
return input.map((item) => keys.reduce((obj, key) => ({ | ||
...obj, | ||
[key]: item[key] | ||
}), {})); | ||
} | ||
return keys.reduce((obj, key) => ({ | ||
...obj, | ||
[key]: input[key] | ||
}), {}); | ||
} | ||
function omit(input, keys) { | ||
if (Array.isArray(input)) { | ||
return input.map((item) => { | ||
const result2 = { ...item }; | ||
keys.forEach((key) => delete result2[key]); | ||
return result2; | ||
}); | ||
} | ||
const result = { ...input }; | ||
keys.forEach((key) => delete result[key]); | ||
return result; | ||
} | ||
function leftPart(s, needle) { | ||
if (s == null) | ||
return null; | ||
let pos = s.indexOf(needle); | ||
return pos == -1 ? s : s.substring(0, pos); | ||
} | ||
function toStr(value) { | ||
return typeof value == "symbol" ? `:${value.description ?? ""}` : `${value}`; | ||
} | ||
function nextParam(params) { | ||
const positionalParams = Object.keys(params).map((x) => x[0] === "_" ? parseInt(x.substring(1)) : NaN).filter((x) => !isNaN(x)); | ||
return "_" + (positionalParams.length == 0 ? 1 : Math.max(...positionalParams) + 1); | ||
} | ||
function mergeParams(params, f) { | ||
let sql = f.sql; | ||
if (f.params && typeof f.params == "object") { | ||
for (const [key, value] of Object.entries(f.params)) { | ||
const exists = key in params && key[0] === "_" && !isNaN(parseInt(key.substring(1))); | ||
if (exists) { | ||
const nextvalue = nextParam(params); | ||
sql = sql.replaceAll(`\$${key}`, `\$${nextvalue}`); | ||
params[nextvalue] = value; | ||
} else { | ||
params[key] = value; | ||
} | ||
} | ||
} | ||
return sql; | ||
} | ||
function asType(cls) { | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
const ref = cls.$ref ? cls : undefined; | ||
return !cls?.$ref && cls.tables ? cls.table : ref ? ref.$ref.cls : cls; | ||
} | ||
function asRef(cls) { | ||
return typeof cls == "object" && cls.$ref ? cls : undefined; | ||
} | ||
function isTemplateStrings(arg) { | ||
return Array.isArray(arg) && "raw" in arg; | ||
} | ||
function snakeCase(s) { | ||
return (s || "").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(); | ||
} | ||
// src/meta.ts | ||
class Meta { | ||
cls; | ||
static metadata = {}; | ||
constructor(cls) { | ||
this.cls = cls; | ||
if (!cls) | ||
throw new Error(`Class must be provided`); | ||
if (!cls.$type) | ||
throw new Error(`Class ${cls.name ?? cls} have a \$type property`); | ||
} | ||
static assertClass(table) { | ||
if (!table) | ||
throw new Error(`Class must be provided`); | ||
const cls = table?.constructor?.$id ? table?.constructor : table.$id ? table : null; | ||
if (!cls) { | ||
const name = table?.name ?? table?.constructor?.name; | ||
if (!name) | ||
throw new Error(`Class or constructor function required`); | ||
else if (typeof table === "function" || typeof table.constructor === "function") | ||
throw new Error(`${name} is not a class or constructor function`); | ||
else | ||
throw new Error(`${name} does not contain metadata, missing @table?`); | ||
} | ||
return cls; | ||
} | ||
static assertTable(table) { | ||
const cls = Meta.assertClass(table); | ||
if (!cls.$type?.table) { | ||
throw new Error(`${cls.name} does not have a @table annotation`); | ||
} | ||
if (!cls.$props || !cls.$props.find((x) => x.column)) { | ||
throw new Error(`${cls.name} does not have any @column annotations`); | ||
} | ||
return cls; | ||
} | ||
static assertMeta(table) { | ||
const cls = Meta.assertClass(table); | ||
const id = cls.$id; | ||
return Meta.metadata[id] ?? (Meta.metadata[id] = new Meta(Meta.assertTable(cls))); | ||
} | ||
get name() { | ||
return this.cls.$type?.name ?? this.cls.name; | ||
} | ||
get tableName() { | ||
const cls = this.cls; | ||
const ret = cls.$type?.table?.alias ?? cls.$type?.name ?? cls.name; | ||
if (!ret) | ||
throw new Error(`Table name not found for ${cls.name}`); | ||
return ret; | ||
} | ||
get type() { | ||
return this.cls.$type; | ||
} | ||
get table() { | ||
const ret = this.type.table; | ||
if (!ret) | ||
throw new Error(`Table definition not found for ${this.cls.name}`); | ||
return ret; | ||
} | ||
get props() { | ||
return this.cls.$props ?? []; | ||
} | ||
get columns() { | ||
return this.props.filter((x) => x.column).map((x) => x.column); | ||
} | ||
} | ||
// src/converters.ts | ||
function converterFor(converter, ...dataTypes) { | ||
const to = {}; | ||
for (const dataType of dataTypes) { | ||
to[dataType] = converter; | ||
} | ||
return to; | ||
} | ||
class DateTimeConverter { | ||
static instance = new DateTimeConverter; | ||
toDb(value) { | ||
const d = toDate(value); | ||
return d ? d.toISOString() : null; | ||
} | ||
fromDb(value) { | ||
if (!value) | ||
return null; | ||
return toDate(value); | ||
} | ||
} | ||
// src/schema.ts | ||
var DriverRequired = `Missing Driver Implementation, see: https://github.com/litdb/litdb`; | ||
var DriverRequiredProxy = new Proxy({}, { | ||
get: (target, key) => { | ||
throw new Error(DriverRequired); | ||
} | ||
}); | ||
function assertSql(sql) { | ||
if (typeof sql != "object" || !sql.sql) { | ||
const desc = typeof sql == "symbol" ? sql.description : Array.isArray(sql) ? "Array" : `${sql}`; | ||
throw new Error(`Expected ${"sql`...`"} fragment, received: ${desc}`); | ||
} | ||
return sql; | ||
} | ||
class Schema { | ||
dialect; | ||
constructor(dialect) { | ||
this.dialect = dialect; | ||
} | ||
converters = { | ||
...converterFor(DateTimeConverter.instance, "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ") | ||
}; | ||
sqlTableNames(schema) { | ||
throw new Error(DriverRequired); | ||
} | ||
sqlColumnDefinition(column) { | ||
throw new Error(DriverRequired); | ||
} | ||
sqlIndexDefinition(table, column) { | ||
throw new Error(DriverRequired); | ||
} | ||
dropTable(table) { | ||
const meta = Meta.assertMeta(table); | ||
let sql = `DROP TABLE IF EXISTS ${this.dialect.quoteTable(meta.tableName)}`; | ||
return sql; | ||
} | ||
createTable(table) { | ||
const meta = Meta.assertMeta(table); | ||
const columns = meta.columns; | ||
let sqlColumns = columns.map((c) => `${this.sqlColumnDefinition(c)}`).join(",\n "); | ||
let sql = `CREATE TABLE ${this.dialect.quoteTable(meta.tableName)} (\n ${sqlColumns}\n);\n`; | ||
const indexes = columns.filter((c) => c.index).map((c) => `${this.sqlIndexDefinition(meta.table, c)};`); | ||
if (indexes.length > 0) { | ||
sql += indexes.join("\n"); | ||
} | ||
return sql; | ||
} | ||
insert(table, options) { | ||
const meta = Meta.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
if (options?.onlyProps) { | ||
props = props.filter((c) => options.onlyProps.includes(c.name)); | ||
} | ||
let columns = props.map((x) => x.column).filter((c) => !c.autoIncrement); | ||
let sqlColumns = columns.map((c) => `${this.dialect.quoteColumn(c.name)}`).join(", "); | ||
let sqlParams = columns.map((c) => `\$${c.name}`).join(", "); | ||
let sql = `INSERT INTO ${this.dialect.quoteTable(meta.tableName)} (${sqlColumns}) VALUES (${sqlParams})`; | ||
return sql; | ||
} | ||
update(table, options) { | ||
const meta = Meta.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
if (options?.onlyProps) { | ||
props = props.filter((c) => options.onlyProps.includes(c.name) || c.column?.primaryKey); | ||
} | ||
const columns = props.map((x) => x.column); | ||
const setColumns = columns.filter((c) => !c.primaryKey); | ||
const whereColumns = columns.filter((c) => c.primaryKey); | ||
const setSql = setColumns.map((c) => `${this.dialect.quoteColumn(c.name)}=\$${c.name}`).join(", "); | ||
const whereSql = whereColumns.map((c) => `${this.dialect.quoteColumn(c.name)} = \$${c.name}`).join(" AND "); | ||
let sql = `UPDATE ${this.dialect.quoteTable(meta.tableName)} SET ${setSql}`; | ||
if (whereSql) { | ||
sql += ` WHERE ${whereSql}`; | ||
} else { | ||
throw new Error(`No WHERE clause exists for UPDATE ${meta.tableName}`); | ||
} | ||
return sql; | ||
} | ||
delete(table, options) { | ||
const meta = Meta.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
const columns = props.map((x) => x.column); | ||
const whereColumns = columns.filter((c) => c.primaryKey); | ||
let whereSql = whereColumns.map((c) => `${this.dialect.quoteColumn(c.name)} = \$${c.name}`).join(" AND "); | ||
if (options?.where) { | ||
let sql2 = whereSql ? " AND " : " WHERE "; | ||
const where = Array.isArray(options.where) ? options.where : [options.where]; | ||
whereSql += sql2 + where.join(" AND "); | ||
} | ||
let sql = `DELETE FROM ${this.dialect.quoteTable(meta.tableName)}`; | ||
if (whereSql) { | ||
sql += ` WHERE ${whereSql}`; | ||
} else { | ||
throw new Error(`No WHERE clause exists for DELETE ${meta.tableName}`); | ||
} | ||
return sql; | ||
} | ||
toDbBindings(table) { | ||
const values = []; | ||
const meta = Meta.assertMeta(table.constructor); | ||
const props = meta.props.filter((x) => x.column); | ||
props.forEach((x) => { | ||
const value = table[x.column.name]; | ||
const converter = this.converters[x.column.type]; | ||
if (converter) { | ||
const dbValue = converter.toDb(value); | ||
values.push(dbValue); | ||
} else { | ||
values.push(value); | ||
} | ||
}); | ||
return values; | ||
} | ||
toDbObject(table, options) { | ||
const values = {}; | ||
const meta = Meta.assertMeta(table.constructor); | ||
const props = meta.props.filter((x) => x.column); | ||
for (const x of props) { | ||
if (options?.onlyProps && !options.onlyProps.includes(x.name)) | ||
continue; | ||
const value = table[x.name]; | ||
const converter = this.converters[x.column.type]; | ||
if (converter) { | ||
const dbValue = converter.toDb(value); | ||
values[x.column.name] = dbValue; | ||
} else { | ||
values[x.column.name] = value; | ||
} | ||
} | ||
return values; | ||
} | ||
} | ||
// src/connection.ts | ||
class DbConnection { | ||
connection; | ||
driver; | ||
$; | ||
schema; | ||
constructor(connection) { | ||
this.connection = connection; | ||
this.$ = connection.$; | ||
this.driver = connection.driver; | ||
this.schema = connection.driver.schema; | ||
} | ||
get sync() { | ||
if (this.driver.sync == null) { | ||
throw new Error(`${this.$.name} does not support sync APIs`); | ||
} | ||
return this.driver.sync; | ||
} | ||
quote(symbol) { | ||
return this.$.quote(symbol); | ||
} | ||
insert(row, options) { | ||
return Promise.resolve(this.sync.insert(row, options)); | ||
} | ||
insertAll(rows, options) { | ||
return Promise.resolve(this.sync.insertAll(rows, options)); | ||
} | ||
listTables() { | ||
return Promise.resolve(this.sync.listTables()); | ||
} | ||
dropTable(table) { | ||
return Promise.resolve(this.sync.dropTable(table)); | ||
} | ||
createTable(table) { | ||
return Promise.resolve(this.sync.createTable(table)); | ||
} | ||
all(strings, ...params) { | ||
return Promise.resolve(this.sync.all(strings, ...params)); | ||
} | ||
one(strings, ...params) { | ||
return Promise.resolve(this.sync.one(strings, ...params)); | ||
} | ||
column(strings, ...params) { | ||
return Promise.resolve(this.sync.column(strings, ...params)); | ||
} | ||
value(strings, ...params) { | ||
return Promise.resolve(this.sync.value(strings, ...params)); | ||
} | ||
arrays(strings, ...params) { | ||
return Promise.resolve(this.sync.arrays(strings, ...params)); | ||
} | ||
array(strings, ...params) { | ||
return Promise.resolve(this.sync.array(strings, ...params)); | ||
} | ||
exec(strings, ...params) { | ||
return Promise.resolve(this.sync.exec(strings, ...params)); | ||
} | ||
run(strings, ...params) { | ||
return Promise.resolve(this.sync.run(strings, ...params)); | ||
} | ||
prepare(strings, ...params) { | ||
if (isTemplateStrings(strings)) { | ||
let stmt = this.connection.prepare(strings, ...params); | ||
return [stmt, params]; | ||
} else if (typeof strings == "object") { | ||
if ("build" in strings) { | ||
let query = strings.build(); | ||
let stmt = this.connection.prepare(query.sql); | ||
return [stmt, query.params]; | ||
} else if ("sql" in strings) { | ||
let stmt = this.connection.prepare(strings.sql); | ||
return [stmt, strings.params ?? {}]; | ||
} | ||
} | ||
throw new Error(`Invalid argument: ${toStr(strings)}`); | ||
} | ||
} | ||
class SyncDbConnection { | ||
connection; | ||
driver; | ||
$; | ||
schema; | ||
constructor(connection) { | ||
this.connection = connection; | ||
this.$ = connection.$; | ||
this.driver = connection.driver; | ||
this.schema = connection.driver.schema; | ||
} | ||
quote(symbol) { | ||
return this.$.quote(symbol); | ||
} | ||
insert(row, options) { | ||
const ret = { changes: 0, lastInsertRowid: 0 }; | ||
if (!row) | ||
return ret; | ||
const cls = row.constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
const onlyProps = options?.onlyProps ?? propsWithValues(row); | ||
const onlyOptions = { onlyProps }; | ||
let stmt = this.connection.prepareSync(this.schema.insert(cls, onlyOptions)); | ||
const dbRow = this.schema.toDbObject(row, onlyOptions); | ||
return stmt.execSync(dbRow); | ||
} else { | ||
let stmt = this.connection.prepareSync(this.schema.insert(cls)); | ||
const dbRow = this.schema.toDbObject(row); | ||
return stmt.execSync(dbRow); | ||
} | ||
} | ||
insertAll(rows, options) { | ||
const ret = { changes: 0, lastInsertRowid: 0 }; | ||
if (rows.length == 0) | ||
return ret; | ||
const cls = rows[0].constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
for (const row of rows) { | ||
const last = this.insert(row, options); | ||
ret.changes += last.changes; | ||
ret.lastInsertRowid = last.lastInsertRowid; | ||
} | ||
} else { | ||
let last = null; | ||
let stmt = this.connection.prepareSync(this.schema.insert(cls)); | ||
for (const row of rows) { | ||
const dbRow = this.schema.toDbObject(row); | ||
last = stmt.execSync(dbRow); | ||
ret.changes += last.changes; | ||
ret.lastInsertRowid = last.lastInsertRowid; | ||
} | ||
} | ||
return ret; | ||
} | ||
update(row, options) { | ||
const ret = { changes: 0, lastInsertRowid: 0 }; | ||
if (!row) | ||
return ret; | ||
const cls = row.constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
const pkNames = cls.$props.filter((x) => x.column?.primaryKey).map((x) => x.column.name); | ||
const onlyProps = Array.from(new Set([...options?.onlyProps ?? propsWithValues(row), ...pkNames])); | ||
const onlyOptions = { onlyProps }; | ||
let stmt = this.connection.prepareSync(this.schema.update(cls, onlyOptions)); | ||
const dbRow = this.schema.toDbObject(row, onlyOptions); | ||
return stmt.execSync(dbRow); | ||
} else { | ||
let stmt = this.connection.prepareSync(this.schema.update(cls)); | ||
const dbRow = this.schema.toDbObject(row); | ||
return stmt.execSync(dbRow); | ||
} | ||
} | ||
delete(row, options) { | ||
const ret = { changes: 0, lastInsertRowid: 0 }; | ||
if (!row) | ||
return ret; | ||
const cls = row.constructor; | ||
let stmt = this.connection.prepareSync(this.schema.delete(cls, options)); | ||
const meta = Meta.assertMeta(cls); | ||
const pkColumns = meta.props.filter((p) => p.column?.primaryKey); | ||
const onlyProps = pkColumns.map((p) => p.name); | ||
const dbRow = this.schema.toDbObject(row, { onlyProps }); | ||
return stmt.execSync(dbRow); | ||
} | ||
listTables() { | ||
return this.column({ sql: this.schema.sqlTableNames(), params: {} }); | ||
} | ||
dropTable(table) { | ||
let stmt = this.connection.prepareSync(this.schema.dropTable(table)); | ||
return stmt.execSync(); | ||
} | ||
createTable(table) { | ||
let stmt = this.connection.prepareSync(this.schema.createTable(table)); | ||
return stmt.execSync(); | ||
} | ||
prepareSync(strings, ...params) { | ||
if (isTemplateStrings(strings)) { | ||
let stmt = this.connection.prepareSync(strings, ...params); | ||
return [stmt, params, undefined]; | ||
} else if (typeof strings == "object") { | ||
if ("build" in strings) { | ||
let query = strings.build(); | ||
let stmt = this.connection.prepareSync(query.sql); | ||
return [stmt, query.params ?? {}, query.into]; | ||
} else if ("sql" in strings) { | ||
let sql = strings.sql; | ||
let params2 = strings.params ?? {}; | ||
let stmt = this.connection.prepareSync(sql); | ||
return [stmt, params2, strings.into]; | ||
} | ||
} | ||
throw new Error(`Invalid argument: ${toStr(strings)}`); | ||
} | ||
all(strings, ...params) { | ||
const [stmt, p, into] = this.prepareSync(strings, ...params); | ||
if (into) { | ||
const use = stmt.as(into); | ||
return Array.isArray(p) ? use.allSync(...p) : use.allSync(p); | ||
} else { | ||
return Array.isArray(p) ? stmt.allSync(...p) : stmt.allSync(p); | ||
} | ||
} | ||
one(strings, ...params) { | ||
const [stmt, p, into] = this.prepareSync(strings, ...params); | ||
if (into) { | ||
const use = stmt.as(into); | ||
return Array.isArray(p) ? use.oneSync(...p) : use.oneSync(p); | ||
} else { | ||
return Array.isArray(p) ? stmt.oneSync(...p) : stmt.oneSync(p); | ||
} | ||
} | ||
column(strings, ...params) { | ||
const [stmt, p] = this.prepareSync(strings, ...params); | ||
return Array.isArray(p) ? stmt.arraysSync(...p).map((x) => x[0]) : stmt.arraysSync(p).map((x) => x[0]); | ||
} | ||
value(strings, ...params) { | ||
const [stmt, p, into] = this.prepareSync(strings, ...params); | ||
const value = Array.isArray(p) ? stmt.valueSync(...p) : stmt.valueSync(p); | ||
if (into) { | ||
if (into === Boolean) { | ||
return !!value; | ||
} | ||
} | ||
return value; | ||
} | ||
arrays(strings, ...params) { | ||
const [stmt, p] = this.prepareSync(strings, ...params); | ||
return Array.isArray(p) ? stmt.arraysSync(...p) : stmt.arraysSync(p); | ||
} | ||
array(strings, ...params) { | ||
const [stmt, p] = this.prepareSync(strings, ...params); | ||
return Array.isArray(p) ? stmt.arraySync(...p) : stmt.arraySync(p); | ||
} | ||
exec(strings, ...params) { | ||
const [stmt, p] = this.prepareSync(strings, ...params); | ||
return Array.isArray(p) ? stmt.execSync(...p) : stmt.execSync(p); | ||
} | ||
run(strings, ...params) { | ||
const [stmt, p] = this.prepareSync(strings, ...params); | ||
if (Array.isArray(p)) { | ||
stmt.runSync(...p); | ||
} else { | ||
stmt.runSync(p); | ||
} | ||
} | ||
} | ||
class ConnectionBase { | ||
driver; | ||
$; | ||
async; | ||
sync; | ||
schema; | ||
dialect; | ||
constructor(driver) { | ||
this.driver = driver; | ||
this.$ = driver.$; | ||
this.schema = driver.schema; | ||
this.dialect = driver.dialect; | ||
this.async = new DbConnection(this); | ||
this.sync = new SyncDbConnection(this); | ||
} | ||
prepare(sql, ...params) { | ||
throw new Error(DriverRequired); | ||
} | ||
prepareSync(sql, ...params) { | ||
throw new Error(DriverRequired); | ||
} | ||
} | ||
class DefaultStrategy { | ||
tableName(table) { | ||
return table; | ||
} | ||
columnName(column) { | ||
return column; | ||
} | ||
tableFromDef(def) { | ||
return def.alias ?? def.name; | ||
} | ||
} | ||
class SnakeCaseStrategy { | ||
tableName(table) { | ||
return snakeCase(table); | ||
} | ||
columnName(column) { | ||
return snakeCase(column); | ||
} | ||
tableFromDef(def) { | ||
return snakeCase(def.alias ?? def.name); | ||
} | ||
} | ||
class FilterConnection { | ||
db; | ||
fn; | ||
$; | ||
orig; | ||
constructor(db, fn) { | ||
this.db = db; | ||
this.fn = fn; | ||
this.orig = db.connection; | ||
db.connection = this; | ||
this.$ = db.$; | ||
} | ||
get driver() { | ||
return this.db.driver; | ||
} | ||
prepareSync(sql, ...params) { | ||
this.fn(sql, params); | ||
return this.orig.prepareSync(sql, ...params); | ||
} | ||
release() { | ||
this.db.connection = this.orig; | ||
} | ||
} | ||
function useFilter(db, filter) { | ||
return new FilterConnection(db, filter); | ||
} | ||
// src/inspect.ts | ||
function alignLeft(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
let aLen = len + 1 - str.length; | ||
if (aLen <= 0) | ||
return str; | ||
return pad + str + pad.repeat(len + 1 - str.length); | ||
} | ||
function alignCenter(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
if (!str) | ||
str = ""; | ||
let nLen = str.length; | ||
let half = Math.floor(len / 2 - nLen / 2); | ||
let odds = Math.abs(nLen % 2 - len % 2); | ||
return pad.repeat(half + 1) + str + pad.repeat(half + 1 + odds); | ||
} | ||
function alignRight(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
let aLen = len + 1 - str.length; | ||
if (aLen <= 0) | ||
return str; | ||
return pad.repeat(len + 1 - str.length) + str + pad; | ||
} | ||
function alignAuto(obj, len, pad = " ") { | ||
let str = `${obj}`; | ||
if (str.length <= len) { | ||
return typeof obj === "number" ? alignRight(str, len, pad) : alignLeft(str, len, pad); | ||
} | ||
return str; | ||
} | ||
class Inspect { | ||
static dump(obj) { | ||
let to = JSON.stringify(obj, null, 4); | ||
return to.replace(/"/g, ""); | ||
} | ||
static printDump(obj) { | ||
console.log(Inspect.dump(obj)); | ||
} | ||
static dumpTable(rows) { | ||
let mapRows = rows; | ||
let keys = uniqueKeys(mapRows); | ||
let colSizes = {}; | ||
keys.forEach((k) => { | ||
let max = k.length; | ||
mapRows.forEach((row) => { | ||
let col = row[k]; | ||
if (col != null) { | ||
let valSize = `${col}`.length; | ||
if (valSize > max) { | ||
max = valSize; | ||
} | ||
} | ||
}); | ||
colSizes[k] = max; | ||
}); | ||
let colSizesLength = Object.keys(colSizes).length; | ||
let rowWidth = Object.keys(colSizes).map((k) => colSizes[k]).reduce((p, c) => p + c, 0) + colSizesLength * 2 + (colSizesLength + 1); | ||
let sb = []; | ||
sb.push(`+${"-".repeat(rowWidth - 2)}+`); | ||
let head = "|"; | ||
keys.forEach((k) => head += alignCenter(k, colSizes[k]) + "|"); | ||
sb.push(head); | ||
sb.push(`|${"-".repeat(rowWidth - 2)}|`); | ||
mapRows.forEach((row) => { | ||
let to = "|"; | ||
keys.forEach((k) => to += "" + alignAuto(row[k], colSizes[k]) + "|"); | ||
sb.push(to); | ||
}); | ||
sb.push(`+${"-".repeat(rowWidth - 2)}+`); | ||
return sb.join("\n"); | ||
} | ||
static printDumpTable(rows) { | ||
console.log(Inspect.dumpTable(rows)); | ||
} | ||
} | ||
// src/sql.ts | ||
class Sql { | ||
@@ -19,5 +731,5 @@ static ops = { | ||
static opKeys = Object.keys(Sql.ops); | ||
static create(driver) { | ||
static create(dialect) { | ||
function $(strings, ...params) { | ||
if (Array.isArray(strings)) { | ||
if (isTemplateStrings(strings)) { | ||
let sb = ""; | ||
@@ -33,6 +745,12 @@ const sqlParams = {}; | ||
} else if (typeof value == "object" && value.$ref) { | ||
sb += driver.quoteTable(Schema.assertMeta(value.$ref.cls).tableName); | ||
sb += dialect.quoteTable(Meta.assertMeta(value.$ref.cls).tableName); | ||
} else if (typeof value == "object" && typeof value.build == "function") { | ||
const frag = value.build(); | ||
sb += mergeParams(sqlParams, frag).replaceAll("\n", "\n "); | ||
} else if (typeof value == "object" && typeof value.sql == "string") { | ||
const frag = value; | ||
sb += mergeParams(sqlParams, frag).replaceAll("\n", "\n "); | ||
} else if (value) { | ||
const paramIndex = Object.keys(sqlParams).length + 1; | ||
const name = `${paramIndex}`; | ||
const name = `_${paramIndex}`; | ||
sb += `\$${name}`; | ||
@@ -48,13 +766,17 @@ sqlParams[name] = value; | ||
} | ||
function quote(meta, prop) { | ||
$.dialect = dialect; | ||
$.quote = dialect.quote.bind(dialect); | ||
$.quoteColumn = dialect.quoteColumn.bind(dialect); | ||
$.quoteTable = dialect.quoteTable.bind(dialect); | ||
function quoteProp(meta, prop) { | ||
const p = meta.props.find((x) => x.name == prop)?.column; | ||
if (!p) | ||
throw new Error(`${meta.name} does not have a column property ${prop}`); | ||
return driver.quoteColumn(p.name); | ||
return dialect.quoteColumn(p.name); | ||
} | ||
$.ref = function(cls, as) { | ||
const meta = Schema.assertMeta(cls); | ||
const meta = Meta.assertMeta(cls); | ||
if (as == null) | ||
as = driver.quoteTable(meta.tableName); | ||
const get = (target, key) => key == "$ref" ? { cls, as } : Symbol(target.prefix + quote(meta, typeof key == "string" ? key : key.description)); | ||
as = dialect.quoteTable(meta.tableName); | ||
const get = (target, key) => key == "$ref" ? { cls, as } : Symbol(target.prefix + quoteProp(meta, typeof key == "string" ? key : key.description)); | ||
const p = new Proxy({ prefix: as ? as + "." : "", meta }, { get }); | ||
@@ -66,5 +788,37 @@ return p; | ||
}; | ||
$.join = function(...joinTables) { | ||
return new SqlJoinBuilder(driver, ...joinTables); | ||
$.fragment = function(sql, params = {}) { | ||
return typeof sql == "object" ? { sql: mergeParams(params, sql), params } : { sql, params }; | ||
}; | ||
$.from = function(table, alias) { | ||
const cls = asType(table); | ||
const ref = asRef(table) ?? $.ref(table, alias ?? ""); | ||
return new SelectQuery($, [cls], [Meta.assertMeta(cls)], [ref]); | ||
}; | ||
$.update = function(table) { | ||
return new UpdateQuery($, [table], [Meta.assertMeta(table)], [$.ref(table, "")]); | ||
}; | ||
$.deleteFrom = function(table) { | ||
return new DeleteQuery($, [table], [Meta.assertMeta(table)], [$.ref(table, "")]); | ||
}; | ||
$.join = function(...tables) { | ||
return new SqlJoinBuilder($, ...tables); | ||
}; | ||
$.groupBy = function(...tables) { | ||
return new SqlGroupByBuilder($, ...tables); | ||
}; | ||
$.having = function(...tables) { | ||
return new SqlHavingBuilder($, ...tables); | ||
}; | ||
$.orderBy = function(...tables) { | ||
return new SqlOrderByBuilder($, ...tables); | ||
}; | ||
$.idEquals = function hasId(id) { | ||
return (x) => $.fragment($`${x.id} = $id`, { id }); | ||
}; | ||
$.log = function(obj) { | ||
console.log(Inspect.dump(obj)); | ||
}; | ||
$.dump = function(obj) { | ||
console.log(Inspect.dumpTable(obj)); | ||
}; | ||
return $; | ||
@@ -74,60 +828,4 @@ } | ||
// src/utils.ts | ||
function isDate(d) { | ||
return d && Object.prototype.toString.call(d) === "[object Date]" && !isNaN(d); | ||
} | ||
function toDate(s) { | ||
return !s ? null : isDate(s) ? s : s[0] == "/" ? new Date(parseFloat(/Date\(([^)]+)\)/.exec(s)[1])) : new Date(s); | ||
} | ||
function propsWithValues(obj) { | ||
return Object.keys(obj).filter((k) => obj[k] != null); | ||
} | ||
function uniqueKeys(rows) { | ||
let to = []; | ||
rows.forEach((o) => Object.keys(o).forEach((k) => { | ||
if (to.indexOf(k) === -1) { | ||
to.push(k); | ||
} | ||
})); | ||
return to; | ||
} | ||
function toStr(value) { | ||
return typeof value == "symbol" ? `:${value.description ?? ""}` : `${value}`; | ||
} | ||
// src/builders/where.ts | ||
var joinOptions = function(type, cls, options) { | ||
if (typeof options == "object") { | ||
options = options; | ||
return { type, cls, on: options?.on, as: options?.as, params: options?.params }; | ||
} else if (typeof options == "function") { | ||
const builder = options; | ||
const { sql, params } = builder.build(); | ||
return { type, cls, on: sql, params }; | ||
} else | ||
throw new Error(`Invalid Join Option: ${typeof options}`); | ||
}; | ||
var nextParam = function(params) { | ||
const positionalParams = Object.keys(params).map((x) => parseInt(x)).filter((x) => !isNaN(x)); | ||
return positionalParams.length == 0 ? 1 : Math.max(...positionalParams) + 1; | ||
}; | ||
var mergeParams = function(params, f) { | ||
let sql = f.sql; | ||
if (f.params && typeof f.params == "object") { | ||
for (const [key, value] of Object.entries(f.params)) { | ||
const exists = key in params && !isNaN(parseInt(key)); | ||
if (exists) { | ||
const nextvalue = nextParam(params); | ||
sql = sql.replaceAll(`\$${key}`, `\$${nextvalue}`); | ||
params[nextvalue] = value; | ||
} else { | ||
params[key] = value; | ||
} | ||
} | ||
} | ||
return sql; | ||
}; | ||
class SqlJoinBuilder { | ||
driver; | ||
$; | ||
get table() { | ||
@@ -138,14 +836,32 @@ return this.tables[0]; | ||
refs; | ||
$; | ||
exprs = []; | ||
params = {}; | ||
alias = ""; | ||
buildOn; | ||
constructor(driver, ...tables) { | ||
this.driver = driver; | ||
constructor($, ...tables) { | ||
this.$ = $; | ||
this.tables = tables; | ||
this.$ = driver.$; | ||
this.refs = this.tables.map((x) => this.$.ref(x)); | ||
} | ||
on(expr) { | ||
this.buildOn = (refs, params) => mergeParams(params, expr.call(this, ...refs)); | ||
join(expr, ...params) { | ||
return this.add("JOIN", expr, ...params); | ||
} | ||
leftJoin(expr, ...params) { | ||
return this.add("LEFT JOIN", expr, ...params); | ||
} | ||
rightJoin(expr, ...params) { | ||
return this.add("RIGHT JOIN", expr, ...params); | ||
} | ||
fullJoin(expr, ...params) { | ||
return this.add("FULL JOIN", expr, ...params); | ||
} | ||
crossJoin(expr, ...params) { | ||
return this.add("CROSS JOIN", expr, ...params); | ||
} | ||
add(type, expr, ...params) { | ||
if (Array.isArray(expr)) { | ||
this.exprs.push({ type, expr: (_) => this.$(expr, ...params) }); | ||
} else if (typeof expr == "function") { | ||
this.exprs.push({ type, expr: (refs) => expr.call(this, ...refs) }); | ||
} | ||
return this; | ||
@@ -157,27 +873,90 @@ } | ||
} | ||
build(refs, type) { | ||
const params = {}; | ||
build(refs) { | ||
if (this.alias != null) { | ||
refs[0].$ref.as = this.$.ref(refs[0].$ref.cls, this.alias); | ||
} | ||
const on = this.buildOn(refs, params); | ||
return { type, table: this.tables[0].name, as: refs[0].$ref.as, on, params }; | ||
const params = {}; | ||
const sqls = []; | ||
for (const join of this.exprs) { | ||
const result = join.expr(refs); | ||
const prefix = sqls.length ? `${alignRight(join.type, 5)}` : ""; | ||
sqls.push(`${prefix} ${mergeParams(params, result)}`); | ||
} | ||
const on = sqls.join(""); | ||
return { type: this.exprs[0].type, on, params }; | ||
} | ||
} | ||
class SqlBuilderBase { | ||
$; | ||
tables; | ||
params = {}; | ||
exprs = []; | ||
delimiter = ", "; | ||
constructor($, ...tables) { | ||
this.$ = $; | ||
this.tables = tables; | ||
} | ||
add(expr, ...params) { | ||
if (Array.isArray(expr)) { | ||
this.exprs.push((_) => this.$(expr, ...params)); | ||
} else if (typeof expr == "function") { | ||
this.exprs.push((refs) => expr.call(this, ...refs)); | ||
} | ||
return this; | ||
} | ||
build(refs) { | ||
const params = {}; | ||
const sqls = []; | ||
for (const expr of this.exprs) { | ||
const result = expr(refs); | ||
sqls.push(mergeParams(params, result)); | ||
} | ||
const sql = sqls.join(this.delimiter); | ||
return { sql, params }; | ||
} | ||
} | ||
class SqlGroupByBuilder extends SqlBuilderBase { | ||
} | ||
class SqlOrderByBuilder extends SqlBuilderBase { | ||
} | ||
class SqlHavingBuilder extends SqlBuilderBase { | ||
constructor($, ...tables) { | ||
super($, ...tables); | ||
this.delimiter = "\n AND "; | ||
} | ||
} | ||
// src/sql.builders.ts | ||
function joinOptions(type, cls, options, ref) { | ||
if (typeof options == "object") { | ||
if (options?.sql) { | ||
const { sql, params } = options; | ||
return { type, cls, ref, on: sql, params }; | ||
} else { | ||
options = options; | ||
return { type, cls, ref, as: options?.as, on: options?.on, params: options?.params }; | ||
} | ||
} else if (typeof options == "function") { | ||
const builder = options; | ||
const { sql, params } = builder.build(); | ||
return { type, cls, on: sql, params }; | ||
} else | ||
throw new Error(`Invalid Join Option: ${typeof options}`); | ||
} | ||
class WhereQuery { | ||
driver; | ||
$; | ||
tables; | ||
metas; | ||
refs; | ||
constructor(driver, tables, metas, refs) { | ||
this.driver = driver; | ||
constructor($, tables, metas, refs) { | ||
this.$ = $; | ||
this.tables = tables; | ||
this.metas = metas; | ||
this.refs = refs; | ||
if (!driver.$) | ||
throw new Error(`\$ not in Driver: ${driver}`); | ||
this.$ = driver.$; | ||
} | ||
$; | ||
_where = []; | ||
@@ -211,10 +990,7 @@ _joins = []; | ||
} | ||
prevJoin() { | ||
return this.refs[this.refs.length - 1]; | ||
createInstance(table, ref) { | ||
const meta = Meta.assertMeta(table); | ||
ref = ref ?? this.$.ref(table); | ||
return new this.constructor(this.$, [...this.tables, table], [...this.metas, meta], [...this.refs, ref]); | ||
} | ||
createInstance(table) { | ||
const meta = {}; | ||
const ref = this.$.ref(table); | ||
return new this.constructor(this.driver, [...this.tables, table], [...this.metas, meta], [...this.refs, ref]); | ||
} | ||
copyInto(instance) { | ||
@@ -226,6 +1002,12 @@ instance.params = Object.assign({}, this.params); | ||
} | ||
addJoin(join) { | ||
const table = join.cls; | ||
const instance = this.createInstance(table); | ||
clone() { | ||
const instance = new this.constructor(this.$, [...this.tables], [...this.metas], [...this.refs]); | ||
this.copyInto(instance); | ||
return instance; | ||
} | ||
addJoin(options) { | ||
const table = options.cls; | ||
const ref = options?.ref ?? (options.as ? this.$.ref(table, options.as) : undefined); | ||
const instance = this.createInstance(table, ref); | ||
this.copyInto(instance); | ||
let q = instance; | ||
@@ -237,10 +1019,10 @@ if (!q.refs[0].$ref.as) { | ||
const qProtected = q; | ||
if (typeof join.on == "string") { | ||
on = join.params ? qProtected.mergeParams({ sql: join.on, params: join.params }) : join.on; | ||
} else if (typeof join.on == "function") { | ||
if (typeof options.on == "string") { | ||
on = options.params ? qProtected.mergeParams({ sql: options.on, params: options.params }) : options.on; | ||
} else if (typeof options.on == "function") { | ||
const refs = q.refs.slice(-2).concat([q.ref]); | ||
const sql = Schema.assertSql(join.on.call(q, ...refs)); | ||
const sql = assertSql(options.on.call(q, ...refs)); | ||
on = qProtected.mergeParams(sql); | ||
} | ||
qProtected._joins.push({ type: join.type, table: Schema.assertMeta(table).tableName, on, params: join.params }); | ||
qProtected._joins.push({ type: options.type, table, on, params: options.params }); | ||
return instance; | ||
@@ -253,3 +1035,3 @@ } | ||
const refs = builder.tables.map((cls2) => this.refOf(cls2) ?? this.$.ref(cls2)); | ||
let { type, table, on, as, params } = builder.build(refs, typeHint); | ||
let { type, on, params } = builder.build(refs, typeHint); | ||
if (on && params) { | ||
@@ -259,19 +1041,29 @@ on = this.mergeParams({ sql: on, params }); | ||
const qProtected = q; | ||
qProtected._joins.push({ type, table, on, as, params }); | ||
qProtected._joins.push({ type, on, params }); | ||
return q; | ||
} | ||
join(cls, options) { | ||
return cls.tables ? this.joinBuilder(cls, "JOIN") : this.addJoin(joinOptions("JOIN", cls, options)); | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
return !cls?.$ref && cls.tables ? this.joinBuilder(cls, "JOIN") : this.addJoin(joinOptions("JOIN", asType(cls), options, asRef(cls))); | ||
} | ||
leftJoin(cls, options) { | ||
return cls.tables ? this.joinBuilder(cls, "LEFT JOIN") : this.addJoin(joinOptions("LEFT JOIN", cls, options)); | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
return this.addJoin(joinOptions("LEFT JOIN", asType(cls), options, asRef(cls))); | ||
} | ||
rightJoin(cls, options) { | ||
return cls.tables ? this.joinBuilder(cls, "RIGHT JOIN") : this.addJoin(joinOptions("RIGHT JOIN", cls, options)); | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
return this.addJoin(joinOptions("RIGHT JOIN", asType(cls), options, asRef(cls))); | ||
} | ||
fullJoin(cls, options) { | ||
return cls.tables ? this.joinBuilder(cls, "FULL JOIN") : this.addJoin(joinOptions("FULL JOIN", cls, options)); | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
return this.addJoin(joinOptions("FULL JOIN", asType(cls), options, asRef(cls))); | ||
} | ||
crossJoin(cls, options) { | ||
return cls.tables ? this.joinBuilder(cls, "CROSS JOIN") : this.addJoin(joinOptions("CROSS JOIN", cls, options)); | ||
if (typeof cls != "object" && typeof cls != "function") | ||
throw new Error(`invalid argument: ${typeof cls}`); | ||
return this.addJoin(joinOptions("CROSS JOIN", asType(cls), options, asRef(cls))); | ||
} | ||
@@ -285,7 +1077,7 @@ where(options, ...params) { | ||
return this; | ||
} else if (Array.isArray(options)) { | ||
return this.condition("AND", { sql: this.$(options, ...params) }); | ||
} else if (isTemplateStrings(options)) { | ||
return this.condition("AND", this.$(options, ...params)); | ||
} else if (typeof options == "function") { | ||
const sql = Schema.assertSql(options.call(this, ...this.refs)); | ||
return this.condition("AND", { sql }); | ||
const sql = assertSql(options.call(this, ...this.refs)); | ||
return this.condition("AND", sql); | ||
} else { | ||
@@ -300,6 +1092,6 @@ return this.condition("AND", options); | ||
} else if (Array.isArray(options)) { | ||
return this.condition("OR", { sql: this.$(options, ...params) }); | ||
return this.condition("OR", this.$(options, ...params)); | ||
} else if (typeof options == "function") { | ||
const sql = Schema.assertSql(options.call(this, ...this.refs)); | ||
return this.condition("OR", { sql }); | ||
const sql = assertSql(options.call(this, ...this.refs)); | ||
return this.condition("OR", sql); | ||
} else { | ||
@@ -310,9 +1102,5 @@ return this.condition("OR", options); | ||
condition(condition, options) { | ||
if (options.sql) { | ||
const sql = Array.isArray(options.sql) ? options.sql : [options.sql]; | ||
for (const fragment of sql) { | ||
this._where.push({ condition, sql: this.mergeParams(fragment) }); | ||
} | ||
} | ||
if (options.rawSql) { | ||
if ("sql" in options && "params" in options) { | ||
this._where.push({ condition, sql: this.mergeParams(options) }); | ||
} else if (options.rawSql) { | ||
const sql = Array.isArray(options.rawSql) ? options.rawSql : [options.rawSql]; | ||
@@ -335,6 +1123,6 @@ for (const fragment of sql) { | ||
quote(symbol) { | ||
return this.driver.quote(symbol); | ||
return this.$.quote(symbol); | ||
} | ||
quoteTable(table) { | ||
return this.driver.quoteTable(table); | ||
return this.$.quoteTable(table); | ||
} | ||
@@ -344,5 +1132,5 @@ quoteColumn(column) { | ||
const prefix = as ? as + "." : ""; | ||
return prefix + this.driver.quoteColumn(column); | ||
return prefix + this.$.quoteColumn(column); | ||
} | ||
alias(alias) { | ||
as(alias) { | ||
this.refs[0] = this.$.ref(this.refs[0].$ref.cls, alias); | ||
@@ -362,8 +1150,7 @@ return this; | ||
for (const [key, value] of Object.entries(f.params)) { | ||
const exists = key in this.params && !isNaN(parseInt(key)); | ||
const exists = key in this.params && key[0] === "_" && !isNaN(parseInt(key.substring(1))); | ||
if (exists) { | ||
const positionalParams = Object.keys(this.params).map((x) => parseInt(x)).filter((x) => !isNaN(x)); | ||
const nextParam2 = positionalParams.length == 0 ? 1 : Math.max(...positionalParams) + 1; | ||
sql = sql.replaceAll(`\$${key}`, `\$${nextParam2}`); | ||
this.params[nextParam2] = value; | ||
const nextvalue = nextParam(this.params); | ||
sql = sql.replaceAll(`\$${key}`, `\$${nextvalue}`); | ||
this.params[nextvalue] = value; | ||
} else { | ||
@@ -395,3 +1182,3 @@ this.params[key] = value; | ||
} | ||
const sql = columnNames.map((name) => `${this.driver.quoteColumn(name)} ${Sql.ops[op]}`).join(` ${condition} `); | ||
const sql = columnNames.map((name) => `${this.$.quoteColumn(name)} ${Sql.ops[op]}`).join(` ${condition} `); | ||
this._where.push({ condition, sql }); | ||
@@ -405,3 +1192,3 @@ } else if (typeof values == "object") { | ||
throw new Error(`Property ${key} is not a column`); | ||
const sqlLeft = `${this.driver.quoteColumn(prop.column.name)} ${sqlOp}`; | ||
const sqlLeft = `${this.$.quoteColumn(prop.column.name)} ${sqlOp}`; | ||
if (Array.isArray(value)) { | ||
@@ -429,6 +1216,6 @@ let sqlValues = ``; | ||
return ""; | ||
let sb = " WHERE "; | ||
let sb = "\n WHERE "; | ||
for (const [i, { condition, sql }] of this._where.entries()) { | ||
if (i > 0) | ||
sb += ` ${condition} `; | ||
sb += `\n${alignRight(condition, 5)}`; | ||
sb += sql; | ||
@@ -442,33 +1229,95 @@ } | ||
let sql = ""; | ||
for (const { type, table, as, on } of this._joins) { | ||
const quotedTable = this.driver.quoteTable(table); | ||
const sqlAs = as && as !== quotedTable ? ` ${as}` : ""; | ||
for (let i = 0;i < this._joins.length; i++) { | ||
const { type, on } = this._joins[i]; | ||
const ref = this.refs[i + 1]; | ||
const meta = this.metas[i + 1]; | ||
const quotedTable = this.$.quoteTable(meta.tableName); | ||
const refAs = ref.$ref.as; | ||
const sqlAs = refAs && refAs !== quotedTable ? ` ${refAs}` : ""; | ||
const sqlOn = typeof on == "string" ? ` ON ${on}` : ""; | ||
sql += ` ${type ?? "JOIN"} ${quotedTable}${sqlAs}${sqlOn}`; | ||
let joinType = type ?? "JOIN"; | ||
const spaces = leftPart(joinType, " ").length <= 4 ? " " : " "; | ||
sql += `\n${spaces}${type ?? "JOIN"} ${quotedTable}${sqlAs}${sqlOn}`; | ||
} | ||
return sql; | ||
} | ||
into(into) { | ||
const { sql, params } = this.build(); | ||
return { sql, params, into }; | ||
} | ||
build() { | ||
const sql = this.buildWhere(); | ||
return { sql, params: this.params }; | ||
const params = this.params; | ||
return { sql, params }; | ||
} | ||
} | ||
// src/builders/delete.ts | ||
class DeleteQuery extends WhereQuery { | ||
buildDelete() { | ||
const sql = `DELETE FROM ${this.quoteTable(this.meta.tableName)}${this.buildWhere()}`; | ||
return sql; | ||
} | ||
build() { | ||
const sql = this.buildDelete(); | ||
return { sql, params: this.params }; | ||
} | ||
} | ||
// src/builders/select.ts | ||
class SelectQuery extends WhereQuery { | ||
_select = []; | ||
_groupBy = []; | ||
_having = []; | ||
_orderBy = []; | ||
_skip; | ||
_take; | ||
_limit; | ||
copyInto(instance) { | ||
super.copyInto(instance); | ||
instance._select = Array.from(this._select); | ||
instance._groupBy = Array.from(this._groupBy); | ||
instance._having = Array.from(this._having); | ||
instance._skip = this._skip; | ||
instance._take = this._take; | ||
return instance; | ||
} | ||
clone() { | ||
return super.clone(); | ||
} | ||
groupBy(options, ...params) { | ||
if (!options && params.length == 0) { | ||
this._groupBy.length = 0; | ||
} else if (Array.isArray(options)) { | ||
const frag = this.$(options, ...params); | ||
this._groupBy.push(this.mergeParams(frag)); | ||
} else if (typeof options == "object") { | ||
const frag = typeof options.build == "function" ? options.build(this.refs) : assertSql(options); | ||
this._groupBy.push(this.mergeParams(frag)); | ||
} else if (typeof options == "function") { | ||
const frag = assertSql(options.call(this, ...this.refs)); | ||
this._groupBy.push(this.mergeParams(frag)); | ||
} else | ||
throw new Error(`Invalid Argument: ${typeof options}`); | ||
return this; | ||
} | ||
having(options, ...params) { | ||
if (!options && params.length == 0) { | ||
this._having.length = 0; | ||
} else if (Array.isArray(options)) { | ||
const frag = this.$(options, ...params); | ||
this._having.push(this.mergeParams(frag)); | ||
} else if (typeof options == "object") { | ||
const frag = typeof options.build == "function" ? options.build(this.refs) : assertSql(options); | ||
this._having.push(this.mergeParams(frag)); | ||
} else if (typeof options == "function") { | ||
const frag = assertSql(options.call(this, ...this.refs)); | ||
this._having.push(this.mergeParams(frag)); | ||
} else | ||
throw new Error(`Invalid Argument: ${typeof options}`); | ||
return this; | ||
} | ||
orderBy(options, ...params) { | ||
if (!options && params.length == 0) { | ||
this._orderBy.length = 0; | ||
} else if (Array.isArray(options)) { | ||
const frag = this.$(options, ...params); | ||
this._orderBy.push(this.mergeParams(frag)); | ||
} else if (typeof options == "object") { | ||
const frag = typeof options.build == "function" ? options.build(this.refs) : assertSql(options); | ||
this._orderBy.push(this.mergeParams(frag)); | ||
} else if (typeof options == "function") { | ||
const frag = assertSql(options.call(this, ...this.refs)); | ||
this._orderBy.push(this.mergeParams(frag)); | ||
} else | ||
throw new Error(`Invalid Argument: ${typeof options}`); | ||
return this; | ||
} | ||
select(options, ...params) { | ||
@@ -487,7 +1336,5 @@ if (!options && params.length === 0) { | ||
if (o.sql) { | ||
const sql = Array.isArray(o.sql) ? o.sql : [o.sql]; | ||
for (const fragment of sql) { | ||
this._select.push(fragment.sql); | ||
this.addParams(fragment.params); | ||
} | ||
const frag = o.sql; | ||
this._select.push(frag.sql); | ||
this.addParams(frag.params); | ||
} | ||
@@ -508,3 +1355,3 @@ if (o.props) { | ||
} else if (typeof options == "function") { | ||
const sql = Schema.assertSql(options.call(this, ...this.refs)); | ||
const sql = assertSql(options.call(this, ...this.refs)); | ||
this._select.push(this.mergeParams(sql)); | ||
@@ -519,14 +1366,28 @@ } else | ||
skip(rows) { | ||
this._skip = rows == null ? undefined : rows; | ||
return this; | ||
return this.limit(this._take, rows); | ||
} | ||
take(rows) { | ||
this._take = rows == null ? undefined : rows; | ||
return this; | ||
return this.limit(rows, this._skip); | ||
} | ||
limit(skip, take) { | ||
limit(take, skip) { | ||
this._take = take == null ? undefined : take; | ||
this._skip = skip == null ? undefined : skip; | ||
this._take = take == null ? undefined : take; | ||
if (take == null && skip == null) { | ||
this._limit = undefined; | ||
} else { | ||
const frag = this.$.dialect.sqlLimit(this._skip, this._take); | ||
this._limit = this.mergeParams(frag); | ||
} | ||
return this; | ||
} | ||
exists() { | ||
const q = this.clone(); | ||
q._select = ["TRUE"]; | ||
q._limit = "LIMIT 1"; | ||
return q.into(Boolean); | ||
} | ||
rowCount() { | ||
const { sql, params } = this.build(); | ||
return { sql: `SELECT COUNT(*) FROM (${sql}) AS COUNT`, params, into: Number }; | ||
} | ||
buildSelect() { | ||
@@ -539,3 +1400,3 @@ const sqlSelect = this._select.length > 0 ? this._select.join(", ") : this.meta.columns.map((x) => this.quoteColumn(x.name)).join(", "); | ||
const quotedTable = this.quoteTable(this.meta.tableName); | ||
let sql = `FROM ${quotedTable}`; | ||
let sql = `\n FROM ${quotedTable}`; | ||
const alias = this.refs[0].$ref.as; | ||
@@ -548,47 +1409,56 @@ if (alias && alias != quotedTable) { | ||
buildGroupBy() { | ||
return ""; | ||
if (this._groupBy.length == 0) | ||
return ""; | ||
return `\n GROUP BY ${this._groupBy.join(", ")}`; | ||
} | ||
buildHaving() { | ||
return ""; | ||
if (this._having.length == 0) | ||
return ""; | ||
return `\n HAVING ${this._having.join("\n AND ")}`; | ||
} | ||
buildOrderBy() { | ||
if (this._orderBy.length == 0) | ||
return ""; | ||
return `\n ORDER BY ${this._orderBy.join(", ")}`; | ||
} | ||
buildLimit() { | ||
const sql = this.driver.sqlLimit(this._skip, this._take); | ||
return sql; | ||
return this._limit ? `\n ${this._limit}` : ""; | ||
} | ||
build() { | ||
const sql = `${this.buildSelect()} ${this.buildFrom()}${this.buildJoins()}${this.buildWhere()}${this.buildGroupBy()}${this.buildHaving()}`; | ||
return { sql, params: this.params }; | ||
let sql = this.buildSelect() + this.buildFrom() + this.buildJoins() + this.buildWhere() + this.buildGroupBy() + this.buildHaving() + this.buildOrderBy() + this.buildLimit(); | ||
const params = this.params; | ||
const into = this._select.length == 0 ? this.tables[0] : undefined; | ||
return { sql, params, into }; | ||
} | ||
} | ||
// src/builders/update.ts | ||
class UpdateQuery extends WhereQuery { | ||
_set = []; | ||
set(options) { | ||
set(options, ...params) { | ||
if (!options) { | ||
this._set.length = 0; | ||
} else if (options.sql) { | ||
const sql = Array.isArray(options.sql) ? options.sql : [options.sql]; | ||
for (const fragment of sql) { | ||
this._set.push(fragment.sql); | ||
this.addParams(fragment.params); | ||
} | ||
} | ||
if (options.rawSql) { | ||
const sql = Array.isArray(options.rawSql) ? options.rawSql : [options.rawSql]; | ||
for (const fragment of sql) { | ||
this._set.push(fragment); | ||
if (isTemplateStrings(options)) { | ||
const frag = this.$(options, ...params); | ||
this._set.push(this.mergeParams(frag)); | ||
} else if (typeof options == "object") { | ||
if ("sql" in options) { | ||
const frag = options; | ||
this._set.push(this.mergeParams(frag)); | ||
} else { | ||
for (const [key, value] of Object.entries(options)) { | ||
const prop = this.meta.props.find((x) => x.name === key); | ||
if (!prop) | ||
throw new Error(`Property ${key} not found in ${this.meta.name}`); | ||
if (!prop.column) | ||
throw new Error(`Property ${key} is not a column`); | ||
this.params[prop.name] = value; | ||
this._set.push(`${this.$.quote(prop.column.name)} = \$${prop.name}`); | ||
} | ||
} | ||
} | ||
if (options.values) { | ||
for (const [key, value] of Object.entries(options.values)) { | ||
const prop = this.meta.props.find((x) => x.name === key); | ||
if (!prop) | ||
throw new Error(`Property ${key} not found in ${this.meta.name}`); | ||
if (!prop.column) | ||
throw new Error(`Property ${key} is not a column`); | ||
this.params[prop.name] = value; | ||
this._set.push(`${this.driver.quote(prop.column.name)} = \$${prop.name}`); | ||
} | ||
} | ||
} else if (typeof options == "function") { | ||
const frag = assertSql(options.call(this, ...this.refs)); | ||
this._set.push(this.mergeParams(frag)); | ||
} else | ||
throw new Error(`invalid argument: ${typeof options}`); | ||
return this; | ||
@@ -610,463 +1480,13 @@ } | ||
// src/connection.ts | ||
class Meta3 { | ||
cls; | ||
constructor(cls) { | ||
this.cls = cls; | ||
if (!cls) | ||
throw new Error(`Class must be provided`); | ||
if (!cls.$type) | ||
throw new Error(`Class ${cls.name ?? cls} have a \$type property`); | ||
} | ||
get name() { | ||
return this.cls.$type?.name ?? this.cls.name; | ||
} | ||
get tableName() { | ||
const cls = this.cls; | ||
const ret = cls.$type?.table?.alias ?? cls.$type?.name ?? cls.name; | ||
if (!ret) | ||
throw new Error(`Table name not found for ${cls.name}`); | ||
return ret; | ||
} | ||
get type() { | ||
return this.cls.$type; | ||
} | ||
get table() { | ||
const ret = this.type.table; | ||
if (!ret) | ||
throw new Error(`Table definition not found for ${this.cls.name}`); | ||
return ret; | ||
} | ||
get props() { | ||
return this.cls.$props ?? []; | ||
} | ||
get columns() { | ||
return this.props.filter((x) => x.column).map((x) => x.column); | ||
} | ||
} | ||
class Schema { | ||
static metadata = {}; | ||
static assertClass(table) { | ||
if (!table) | ||
throw new Error(`Class must be provided`); | ||
const cls = table?.constructor?.$id ? table?.constructor : table.$id ? table : null; | ||
if (!cls) { | ||
const name = table?.name ?? table?.constructor?.name; | ||
if (!name) | ||
throw new Error(`Class or constructor function required`); | ||
else if (typeof table === "function" || typeof table.constructor === "function") | ||
throw new Error(`${name} is not a class or constructor function`); | ||
else | ||
throw new Error(`${name} does not contain metadata, missing @table?`); | ||
} | ||
return cls; | ||
} | ||
static assertTable(table) { | ||
const cls = Schema.assertClass(table); | ||
if (!cls.$type?.table) { | ||
throw new Error(`${cls.name} does not have a @table annotation`); | ||
} | ||
if (!cls.$props || !cls.$props.find((x) => x.column)) { | ||
throw new Error(`${cls.name} does not have any @column annotations`); | ||
} | ||
return cls; | ||
} | ||
static assertMeta(table) { | ||
const cls = Schema.assertClass(table); | ||
const id = cls.$id; | ||
return Schema.metadata[id] ?? (Schema.metadata[id] = new Meta3(Schema.assertTable(cls))); | ||
} | ||
static assertSql(sql) { | ||
if (typeof sql != "object" || !sql.sql) { | ||
const desc = typeof sql == "symbol" ? sql.description : Array.isArray(sql) ? "Array" : `${sql}`; | ||
throw new Error(`Expected sql\`...\` fragment, received: ${desc}`); | ||
} | ||
class DeleteQuery extends WhereQuery { | ||
buildDelete() { | ||
const sql = `DELETE FROM ${this.quoteTable(this.meta.tableName)}${this.buildWhere()}`; | ||
return sql; | ||
} | ||
static dropTable(table, driver) { | ||
const meta = Schema.assertMeta(table); | ||
let sql = `DROP TABLE IF EXISTS ${driver.quoteTable(meta.tableName)}`; | ||
return sql; | ||
build() { | ||
const sql = this.buildDelete(); | ||
return { sql, params: this.params }; | ||
} | ||
static createTable(table, driver) { | ||
const meta = Schema.assertMeta(table); | ||
const columns = meta.columns; | ||
let sqlColumns = columns.map((c) => `${driver.sqlColumnDefinition(c)}`).join(",\n "); | ||
let sql = `CREATE TABLE ${driver.quoteTable(meta.tableName)} (\n ${sqlColumns}\n);\n`; | ||
const indexes = columns.filter((c) => c.index).map((c) => `${driver.sqlIndexDefinition(meta.table, c)};`); | ||
if (indexes.length > 0) { | ||
sql += indexes.join("\n"); | ||
} | ||
return sql; | ||
} | ||
static insert(table, driver, options) { | ||
const meta = Schema.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
if (options?.onlyProps) { | ||
props = props.filter((c) => options.onlyProps.includes(c.name)); | ||
} | ||
let columns = props.map((x) => x.column).filter((c) => !c.autoIncrement); | ||
let sqlColumns = columns.map((c) => `${driver.quoteColumn(c.name)}`).join(", "); | ||
let sqlParams = columns.map((c) => `\$${c.name}`).join(", "); | ||
let sql = `INSERT INTO ${driver.quoteTable(meta.tableName)} (${sqlColumns}) VALUES (${sqlParams})`; | ||
return sql; | ||
} | ||
static update(table, driver, options) { | ||
const meta = Schema.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
if (options?.onlyProps) { | ||
props = props.filter((c) => options.onlyProps.includes(c.name)); | ||
} | ||
const columns = props.map((x) => x.column); | ||
const setColumns = columns.filter((c) => !c.primaryKey); | ||
const whereColumns = columns.filter((c) => c.primaryKey); | ||
const setSql = setColumns.map((c) => `${driver.quoteColumn(c.name)}=\$${c.name}`).join(", "); | ||
const whereSql = whereColumns.map((c) => `${driver.quoteColumn(c.name)} = \$${c.name}`).join(" AND "); | ||
let sql = `UPDATE ${driver.quoteTable(meta.tableName)} SET ${setSql}`; | ||
if (whereSql) { | ||
sql += ` WHERE ${whereSql}`; | ||
} else if (!options?.force) { | ||
throw new Error(`No WHERE clause exists for UPDATE ${meta.tableName}, force update with { force:true }`); | ||
} | ||
console.log("Schema.update", sql); | ||
return sql; | ||
} | ||
static delete(table, driver, options) { | ||
const meta = Schema.assertMeta(table); | ||
let props = meta.props.filter((x) => x.column); | ||
const columns = props.map((x) => x.column); | ||
const whereColumns = columns.filter((c) => c.primaryKey); | ||
let whereSql = whereColumns.map((c) => `${driver.quoteColumn(c.name)} = \$${c.name}`).join(" AND "); | ||
if (options?.where) { | ||
let sql2 = whereSql ? " AND " : " WHERE "; | ||
const where5 = Array.isArray(options.where) ? options.where : [options.where]; | ||
whereSql += sql2 + where5.join(" AND "); | ||
} | ||
let sql = `DELETE FROM ${driver.quoteTable(meta.tableName)}`; | ||
if (whereSql) { | ||
sql += ` WHERE ${whereSql}`; | ||
} else if (!options?.force) { | ||
throw new Error(`No WHERE clause exists for DELETE ${meta.tableName}, force delete with { force:true }`); | ||
} | ||
console.log("Schema.delete", sql); | ||
return sql; | ||
} | ||
static toDbBindings(table, driver) { | ||
const values = []; | ||
const meta = Schema.assertMeta(table.constructor); | ||
const props = meta.props.filter((x) => x.column); | ||
props.forEach((x) => { | ||
const value = table[x.column.name]; | ||
const converter = driver.converters[x.column.type]; | ||
if (converter) { | ||
const dbValue = converter.toDb(value); | ||
values.push(dbValue); | ||
} else { | ||
values.push(value); | ||
} | ||
}); | ||
return values; | ||
} | ||
static toDbObject(table, driver, options) { | ||
const values = {}; | ||
const meta = Schema.assertMeta(table.constructor); | ||
const props = meta.props.filter((x) => x.column); | ||
for (const x of props) { | ||
if (options?.onlyProps && !options.onlyProps.includes(x.name)) | ||
continue; | ||
const value = table[x.name]; | ||
const converter = driver.converters[x.column.type]; | ||
if (converter) { | ||
const dbValue = converter.toDb(value); | ||
values[x.column.name] = dbValue; | ||
} else { | ||
values[x.column.name] = value; | ||
} | ||
} | ||
return values; | ||
} | ||
} | ||
class ConnectionBase { | ||
driver; | ||
$; | ||
constructor(driver) { | ||
this.driver = driver; | ||
if (!driver.$) | ||
throw new Error(`\$ not in Driver: ${driver}`); | ||
this.$ = driver.$; | ||
} | ||
quote(symbol) { | ||
return this.driver.quote(symbol); | ||
} | ||
from(table) { | ||
return new SelectQuery(this.driver, [table], [Schema.assertMeta(table)], [this.$.ref(table, "")]); | ||
} | ||
updateFor(table) { | ||
return new UpdateQuery(this.driver, [table], [Schema.assertMeta(table)], [this.$.ref(table, "")]); | ||
} | ||
deleteFrom(table) { | ||
return new DeleteQuery(this.driver, [table], [Schema.assertMeta(table)], [this.$.ref(table, "")]); | ||
} | ||
} | ||
class Connection extends ConnectionBase { | ||
get sync() { | ||
if (this.driver.sync == null) { | ||
throw new Error(`${this.driver.name} does not support sync APIs`); | ||
} | ||
return this.driver.sync; | ||
} | ||
quote(symbol) { | ||
return this.driver.quote(symbol); | ||
} | ||
async insert(row, options) { | ||
return Promise.resolve(this.sync.insert(row, options)); | ||
} | ||
async insertAll(rows, options) { | ||
return Promise.resolve(this.sync.insertAll(rows, options)); | ||
} | ||
async listTables() { | ||
return Promise.resolve(this.sync.listTables()); | ||
} | ||
async dropTable(table) { | ||
return Promise.resolve(this.sync.dropTable(table)); | ||
} | ||
async createTable(table) { | ||
return Promise.resolve(this.sync.createTable(table)); | ||
} | ||
async all(strings, ...params) { | ||
return Promise.resolve(this.sync.all(strings, ...params)); | ||
} | ||
async single(strings, ...params) { | ||
return Promise.resolve(this.sync.single(strings, ...params)); | ||
} | ||
} | ||
class SyncConnection extends ConnectionBase { | ||
insert(row, options) { | ||
if (!row) | ||
return; | ||
const cls = row.constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
const onlyProps = options?.onlyProps ?? propsWithValues(row); | ||
const onlyOptions = { onlyProps }; | ||
let stmt = this.driver.prepareRaw(Schema.insert(cls, this.driver, onlyOptions)); | ||
const dbRow = Schema.toDbObject(row, this.driver, onlyOptions); | ||
return stmt.exec(dbRow); | ||
} else { | ||
let stmt = this.driver.prepareRaw(Schema.insert(cls, this.driver)); | ||
const dbRow = Schema.toDbObject(row, this.driver); | ||
return stmt.exec(dbRow); | ||
} | ||
} | ||
insertAll(rows, options) { | ||
if (rows.length == 0) | ||
return; | ||
const cls = rows[0].constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
for (const row of rows) { | ||
this.insert(row, options); | ||
} | ||
} else { | ||
let last = null; | ||
let stmt = this.driver.prepareRaw(Schema.insert(cls, this.driver)); | ||
for (const row of rows) { | ||
const dbRow = Schema.toDbObject(row, this.driver); | ||
last = stmt.exec(dbRow); | ||
} | ||
return last; | ||
} | ||
} | ||
update(row, options) { | ||
if (!row) | ||
return; | ||
const cls = row.constructor; | ||
if (options?.onlyProps || options?.onlyWithValues) { | ||
const onlyProps = options?.onlyProps ?? propsWithValues(row); | ||
const onlyOptions = { onlyProps }; | ||
let stmt = this.driver.prepareRaw(Schema.update(cls, this.driver, onlyOptions)); | ||
const dbRow = Schema.toDbObject(row, this.driver, onlyOptions); | ||
return stmt.exec(dbRow); | ||
} else { | ||
let stmt = this.driver.prepareRaw(Schema.update(cls, this.driver)); | ||
const dbRow = Schema.toDbObject(row, this.driver); | ||
return stmt.exec(dbRow); | ||
} | ||
} | ||
delete(row, options) { | ||
if (!row) | ||
return; | ||
const cls = row.constructor; | ||
let stmt = this.driver.prepareRaw(Schema.delete(cls, this.driver, options)); | ||
const meta = Schema.assertMeta(cls); | ||
const pkColumns = meta.props.filter((p) => p.column?.primaryKey); | ||
const onlyProps = pkColumns.map((p) => p.name); | ||
const dbRow = Schema.toDbObject(row, this.driver, { onlyProps }); | ||
return stmt.exec(dbRow); | ||
} | ||
listTables() { | ||
let stmt = this.driver.prepareRaw(this.driver.sqlTableNames()); | ||
const ret = stmt.columnSync(); | ||
return ret; | ||
} | ||
dropTable(table) { | ||
let stmt = this.driver.prepareRaw(Schema.dropTable(table, this.driver)); | ||
return stmt.exec(); | ||
} | ||
createTable(table) { | ||
let stmt = this.driver.prepareRaw(Schema.createTable(table, this.driver)); | ||
return stmt.exec(); | ||
} | ||
createStatment(strings, ...params) { | ||
if (typeof strings == "object" && "build" in strings) { | ||
let query2 = strings.build(); | ||
let stmt = this.driver.prepareRaw(query2.sql); | ||
return [stmt, query2.params]; | ||
} else { | ||
let stmt = this.driver.prepare(strings, ...params); | ||
return [stmt, params]; | ||
} | ||
} | ||
all(strings, ...params) { | ||
const [stmt, p] = this.createStatment(strings, ...params); | ||
return Array.isArray(p) ? stmt.allSync(...p) : stmt.allSync(p); | ||
} | ||
single(strings, ...params) { | ||
const [stmt, p] = this.createStatment(strings, ...params); | ||
return Array.isArray(p) ? stmt.firstSync(...p) : stmt.firstSync(p); | ||
} | ||
column(strings, ...params) { | ||
const [stmt, p] = this.createStatment(strings, ...params); | ||
return Array.isArray(p) ? stmt.columnSync(...p) : stmt.columnSync(p); | ||
} | ||
scalar(strings, ...params) { | ||
const [stmt, p] = this.createStatment(strings, ...params); | ||
return Array.isArray(p) ? stmt.scalarSync(...p) : stmt.scalarSync(p); | ||
} | ||
exec(sql, params) { | ||
if (!sql) | ||
throw new Error("query is required"); | ||
const query2 = typeof sql == "object" && "build" in sql ? sql.build() : { sql, params }; | ||
let stmt = this.driver.prepareRaw(query2.sql); | ||
return stmt.exec(query2.params); | ||
} | ||
} | ||
class NamingStrategy { | ||
tableName(table) { | ||
return table; | ||
} | ||
columnName(column) { | ||
return column; | ||
} | ||
tableFromDef(def) { | ||
return def.alias ?? def.name; | ||
} | ||
} | ||
// src/inspect.ts | ||
var alignLeft = function(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
let aLen = len + 1 - str.length; | ||
if (aLen <= 0) | ||
return str; | ||
return pad + str + pad.repeat(len + 1 - str.length); | ||
}; | ||
var alignCenter = function(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
if (!str) | ||
str = ""; | ||
let nLen = str.length; | ||
let half = Math.floor(len / 2 - nLen / 2); | ||
let odds = Math.abs(nLen % 2 - len % 2); | ||
return pad.repeat(half + 1) + str + pad.repeat(half + 1 + odds); | ||
}; | ||
var alignRight = function(str, len, pad = " ") { | ||
if (len < 0) | ||
return ""; | ||
let aLen = len + 1 - str.length; | ||
if (aLen <= 0) | ||
return str; | ||
return pad.repeat(len + 1 - str.length) + str + pad; | ||
}; | ||
var alignAuto = function(obj, len, pad = " ") { | ||
let str = `${obj}`; | ||
if (str.length <= len) { | ||
return typeof obj === "number" ? alignRight(str, len, pad) : alignLeft(str, len, pad); | ||
} | ||
return str; | ||
}; | ||
class Inspect { | ||
static dump(obj) { | ||
let to = JSON.stringify(obj, null, 4); | ||
return to.replace(/"/g, ""); | ||
} | ||
static printDump(obj) { | ||
console.log(Inspect.dump(obj)); | ||
} | ||
static dumpTable(rows) { | ||
let mapRows = rows; | ||
let keys = uniqueKeys(mapRows); | ||
let colSizes = {}; | ||
keys.forEach((k) => { | ||
let max = k.length; | ||
mapRows.forEach((row) => { | ||
let col = row[k]; | ||
if (col != null) { | ||
let valSize = `${col}`.length; | ||
if (valSize > max) { | ||
max = valSize; | ||
} | ||
} | ||
}); | ||
colSizes[k] = max; | ||
}); | ||
let colSizesLength = Object.keys(colSizes).length; | ||
let rowWidth = Object.keys(colSizes).map((k) => colSizes[k]).reduce((p, c) => p + c, 0) + colSizesLength * 2 + (colSizesLength + 1); | ||
let sb = []; | ||
sb.push(`+${"-".repeat(rowWidth - 2)}+`); | ||
let head = "|"; | ||
keys.forEach((k) => head += alignCenter(k, colSizes[k]) + "|"); | ||
sb.push(head); | ||
sb.push(`|${"-".repeat(rowWidth - 2)}|`); | ||
mapRows.forEach((row) => { | ||
let to = "|"; | ||
keys.forEach((k) => to += "" + alignAuto(row[k], colSizes[k]) + "|"); | ||
sb.push(to); | ||
}); | ||
sb.push(`+${"-".repeat(rowWidth - 2)}+`); | ||
return sb.join("\n"); | ||
} | ||
static printDumpTable(rows) { | ||
console.log(Inspect.dumpTable(rows)); | ||
} | ||
} | ||
// src/converters.ts | ||
function converterFor(converter, ...dataTypes) { | ||
const to = {}; | ||
for (const dataType of dataTypes) { | ||
to[dataType] = converter; | ||
} | ||
return to; | ||
} | ||
class DateTimeConverter { | ||
static instance = new DateTimeConverter; | ||
toDb(value) { | ||
const d = toDate(value); | ||
return d ? d.toISOString() : null; | ||
} | ||
fromDb(value) { | ||
if (!value) | ||
return null; | ||
return toDate(value); | ||
} | ||
} | ||
// src/model.ts | ||
@@ -1131,3 +1551,3 @@ function table(options) { | ||
var DataType; | ||
(function(DataType2) { | ||
((DataType2) => { | ||
DataType2["INTEGER"] = "INTEGER"; | ||
@@ -1162,3 +1582,3 @@ DataType2["SMALLINT"] = "SMALLINT"; | ||
DataType2["XML"] = "XML"; | ||
})(DataType || (DataType = {})); | ||
})(DataType ||= {}); | ||
var DefaultValues = { | ||
@@ -1171,19 +1591,376 @@ NOW: "{NOW}", | ||
}; | ||
// src/sqlite/dialect.ts | ||
class SqliteDialect { | ||
$; | ||
strategy = new DefaultStrategy; | ||
constructor() { | ||
this.$ = Sql.create(this); | ||
} | ||
quote(name) { | ||
return `"${name}"`; | ||
} | ||
quoteTable(name) { | ||
return this.quote(this.strategy.tableName(name)); | ||
} | ||
quoteColumn(name) { | ||
return this.quote(this.strategy.columnName(name)); | ||
} | ||
sqlLimit(offset, limit) { | ||
if (offset == null && limit == null) | ||
throw new Error(`Invalid argument sqlLimit(${offset}, ${limit})`); | ||
const frag = offset ? this.$.fragment(`LIMIT \$limit OFFSET \$offset`, { offset, limit: limit ?? -1 }) : this.$.fragment(`LIMIT \$limit`, { limit }); | ||
return frag; | ||
} | ||
} | ||
// src/sqlite/schema.ts | ||
class SqliteSchema extends Schema { | ||
driver; | ||
constructor(driver) { | ||
super(driver.dialect); | ||
this.driver = driver; | ||
} | ||
sqlTableNames() { | ||
return "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'"; | ||
} | ||
sqlIndexDefinition(table2, column2) { | ||
const unique = column2.unique ? "UNIQUE INDEX" : "INDEX"; | ||
return `CREATE ${unique} idx_${table2.name}_${column2.name} ON ${this.dialect.quoteTable(table2.name)} (${this.dialect.quoteColumn(column2.name)})`; | ||
} | ||
sqlColumnDefinition(column2) { | ||
let dataType = column2.type; | ||
let type = this.driver.types.native.includes(dataType) ? dataType : undefined; | ||
if (!type) { | ||
for (const [sqliteType, typeMapping] of Object.entries(this.driver.types.map)) { | ||
if (typeMapping.includes(dataType)) { | ||
type = sqliteType; | ||
break; | ||
} | ||
} | ||
} | ||
if (!type) | ||
type = dataType; | ||
let sb = `${this.dialect.quoteColumn(column2.name)} ${type}`; | ||
if (column2.primaryKey) { | ||
sb += " PRIMARY KEY"; | ||
} | ||
if (column2.autoIncrement) { | ||
sb += " AUTOINCREMENT"; | ||
} | ||
if (column2.required) { | ||
sb += " NOT NULL"; | ||
} | ||
if (column2.unique && !column2.index) { | ||
sb += " UNIQUE"; | ||
} | ||
if (column2.defaultValue) { | ||
const val = this.driver.variables[column2.defaultValue] ?? column2.defaultValue; | ||
sb += ` DEFAULT ${val}`; | ||
} | ||
return sb; | ||
} | ||
sqlLimit(offset, limit) { | ||
if (offset == null && limit == null) | ||
throw new Error(`Invalid argument sqlLimit(${offset}, ${limit})`); | ||
const frag = offset ? this.driver.$.fragment(`LIMIT \$limit OFFSET \$offset`, { offset, limit: limit ?? -1 }) : this.driver.$.fragment(`LIMIT \$limit`, { limit }); | ||
return frag; | ||
} | ||
} | ||
// src/sqlite/driver.ts | ||
class SqliteTypes { | ||
native = [ | ||
"INTEGER" /* INTEGER */, | ||
"SMALLINT" /* SMALLINT */, | ||
"BIGINT" /* BIGINT */, | ||
"REAL" /* REAL */, | ||
"DOUBLE" /* DOUBLE */, | ||
"FLOAT" /* FLOAT */, | ||
"NUMERIC" /* NUMERIC */, | ||
"DECIMAL" /* DECIMAL */, | ||
"BOOLEAN" /* BOOLEAN */, | ||
"DATE" /* DATE */, | ||
"DATETIME" /* DATETIME */ | ||
]; | ||
map = { | ||
INTEGER: ["INTERVAL" /* INTERVAL */], | ||
REAL: ["REAL" /* REAL */], | ||
NUMERIC: ["DECIMAL" /* DECIMAL */, "NUMERIC" /* NUMERIC */, "MONEY" /* MONEY */], | ||
BLOB: ["BLOB" /* BLOB */, "BYTES" /* BYTES */, "BIT" /* BIT */], | ||
TEXT: [ | ||
"UUID" /* UUID */, | ||
"JSON" /* JSON */, | ||
"JSONB" /* JSONB */, | ||
"XML" /* XML */, | ||
"TIME" /* TIME */, | ||
"TIMEZ" /* TIMEZ */, | ||
"TIMESTAMP" /* TIMESTAMP */, | ||
"TIMESTAMPZ" /* TIMESTAMPZ */ | ||
] | ||
}; | ||
} | ||
class Sqlite { | ||
static connection; | ||
static driver; | ||
static schema; | ||
static init() { | ||
const c = Sqlite.connection = new SqliteConnection(new Sqlite); | ||
const { driver, schema } = c; | ||
Object.assign(Sqlite, { driver, schema }); | ||
return c; | ||
} | ||
name; | ||
dialect; | ||
schema; | ||
strategy = new DefaultStrategy; | ||
$; | ||
variables = { | ||
[DefaultValues.NOW]: "CURRENT_TIMESTAMP", | ||
[DefaultValues.MAX_TEXT]: "TEXT", | ||
[DefaultValues.MAX_TEXT_UNICODE]: "TEXT", | ||
[DefaultValues.TRUE]: "1", | ||
[DefaultValues.FALSE]: "0" | ||
}; | ||
types; | ||
converters = { | ||
...converterFor(DateTimeConverter.instance, "DATE" /* DATE */, "DATETIME" /* DATETIME */, "TIMESTAMP" /* TIMESTAMP */, "TIMESTAMPZ" /* TIMESTAMPZ */) | ||
}; | ||
constructor() { | ||
this.name = this.constructor.name; | ||
this.dialect = new SqliteDialect; | ||
this.$ = this.dialect.$; | ||
this.types = new SqliteTypes; | ||
this.schema = new SqliteSchema(this); | ||
} | ||
} | ||
class SqliteConnection extends ConnectionBase { | ||
} | ||
// src/mysql/dialect.ts | ||
class MySqlDialect { | ||
$; | ||
strategy = new DefaultStrategy; | ||
constructor() { | ||
this.$ = Sql.create(this); | ||
} | ||
quote(name) { | ||
return "`" + name + "`"; | ||
} | ||
quoteTable(name) { | ||
return this.quote(this.strategy.tableName(name)); | ||
} | ||
quoteColumn(name) { | ||
return this.quote(this.strategy.columnName(name)); | ||
} | ||
sqlLimit(offset, limit) { | ||
if (offset == null && limit == null) | ||
throw new Error(`Invalid argument sqlLimit(${offset}, ${limit})`); | ||
const frag = offset ? limit ? this.$.fragment(`LIMIT \$offset, \$limit`, { offset, limit }) : this.$.fragment(`LIMIT \$offset, 18446744073709551615`, { offset }) : this.$.fragment(`LIMIT \$limit`, { limit }); | ||
return frag; | ||
} | ||
} | ||
// src/mysql/schema.ts | ||
class MySqlSchema extends SqliteSchema { | ||
} | ||
// src/mysql/driver.ts | ||
class MySqlTypes { | ||
native = [ | ||
"INTEGER" /* INTEGER */, | ||
"SMALLINT" /* SMALLINT */, | ||
"BIGINT" /* BIGINT */, | ||
"REAL" /* REAL */, | ||
"DOUBLE" /* DOUBLE */, | ||
"FLOAT" /* FLOAT */, | ||
"DECIMAL" /* DECIMAL */, | ||
"NUMERIC" /* NUMERIC */, | ||
"DECIMAL" /* DECIMAL */, | ||
"MONEY" /* MONEY */, | ||
"BOOLEAN" /* BOOLEAN */, | ||
"DATE" /* DATE */, | ||
"DATETIME" /* DATETIME */, | ||
"TIME" /* TIME */, | ||
"TIMEZ" /* TIMEZ */, | ||
"TIMESTAMP" /* TIMESTAMP */, | ||
"TIMESTAMPZ" /* TIMESTAMPZ */, | ||
"INTERVAL" /* INTERVAL */, | ||
"UUID" /* UUID */, | ||
"JSON" /* JSON */, | ||
"JSONB" /* JSONB */, | ||
"XML" /* XML */, | ||
"BLOB" /* BLOB */, | ||
"BYTES" /* BYTES */, | ||
"BIT" /* BIT */ | ||
]; | ||
map = { | ||
["DOUBLE" /* DOUBLE */]: ["REAL" /* REAL */], | ||
["MONEY" /* MONEY */]: ["DECIMAL" /* DECIMAL */], | ||
["TIME" /* TIME */]: ["TIMEZ" /* TIMEZ */], | ||
["TIMESTAMP" /* TIMESTAMP */]: ["TIMESTAMPZ" /* TIMESTAMPZ */], | ||
["INTEGER" /* INTEGER */]: ["INTERVAL" /* INTERVAL */], | ||
["JSON" /* JSON */]: ["JSONB" /* JSONB */], | ||
["TEXT" /* TEXT */]: ["XML" /* XML */], | ||
BINARY: ["BYTES" /* BYTES */], | ||
"BINARY(1)": ["BIT" /* BIT */] | ||
}; | ||
} | ||
class MySql extends Sqlite { | ||
static connection; | ||
static driver; | ||
static schema; | ||
static init() { | ||
const c = MySql.connection = new MySqlConnection(new MySql); | ||
const { driver, schema } = c; | ||
Object.assign(MySql, { driver, schema }); | ||
return c; | ||
} | ||
constructor() { | ||
super(); | ||
this.dialect = new MySqlDialect; | ||
this.$ = this.dialect.$; | ||
this.types = new MySqlTypes; | ||
this.schema = new MySqlSchema(this); | ||
} | ||
} | ||
class MySqlConnection extends ConnectionBase { | ||
} | ||
// src/postgres/dialect.ts | ||
class PostgreSqlDialect { | ||
$; | ||
strategy = new DefaultStrategy; | ||
constructor() { | ||
this.$ = Sql.create(this); | ||
} | ||
quote(name) { | ||
return `"${name}"`; | ||
} | ||
quoteTable(name) { | ||
return this.quote(this.strategy.tableName(name)); | ||
} | ||
quoteColumn(name) { | ||
return this.quote(this.strategy.columnName(name)); | ||
} | ||
sqlLimit(offset, limit) { | ||
if (offset == null && limit == null) | ||
throw new Error(`Invalid argument sqlLimit(${offset}, ${limit})`); | ||
const frag = offset ? limit ? this.$.fragment(`LIMIT \$limit OFFSET \$offset`, { offset, limit }) : this.$.fragment(`OFFSET \$offset`, { offset }) : this.$.fragment(`LIMIT \$limit`, { limit }); | ||
return frag; | ||
} | ||
} | ||
// src/postgres/schema.ts | ||
class PostgreSqlSchema extends SqliteSchema { | ||
} | ||
// src/postgres/driver.ts | ||
class PostgreSqlTypes { | ||
native = [ | ||
"INTEGER" /* INTEGER */, | ||
"SMALLINT" /* SMALLINT */, | ||
"BIGINT" /* BIGINT */, | ||
"REAL" /* REAL */, | ||
"DOUBLE" /* DOUBLE */, | ||
"FLOAT" /* FLOAT */, | ||
"DECIMAL" /* DECIMAL */, | ||
"NUMERIC" /* NUMERIC */, | ||
"DECIMAL" /* DECIMAL */, | ||
"MONEY" /* MONEY */, | ||
"BOOLEAN" /* BOOLEAN */, | ||
"DATE" /* DATE */, | ||
"DATETIME" /* DATETIME */, | ||
"TIME" /* TIME */, | ||
"TIMEZ" /* TIMEZ */, | ||
"TIMESTAMP" /* TIMESTAMP */, | ||
"TIMESTAMPZ" /* TIMESTAMPZ */, | ||
"INTERVAL" /* INTERVAL */, | ||
"UUID" /* UUID */, | ||
"JSON" /* JSON */, | ||
"JSONB" /* JSONB */, | ||
"XML" /* XML */, | ||
"BLOB" /* BLOB */, | ||
"BYTES" /* BYTES */, | ||
"BIT" /* BIT */ | ||
]; | ||
map = {}; | ||
} | ||
class PostgreSql extends Sqlite { | ||
static connection; | ||
static driver; | ||
static schema; | ||
static init() { | ||
const c = PostgreSql.connection = new PostgreSqlConnection(new PostgreSql); | ||
const { driver, schema } = c; | ||
Object.assign(PostgreSql, { driver, schema }); | ||
return c; | ||
} | ||
constructor() { | ||
super(); | ||
this.dialect = new PostgreSqlDialect; | ||
this.$ = this.dialect.$; | ||
this.types = new PostgreSqlTypes; | ||
this.schema = new PostgreSqlSchema(this); | ||
} | ||
} | ||
class PostgreSqlConnection extends ConnectionBase { | ||
} | ||
// src/index.ts | ||
var sqlite = (() => { | ||
return Sqlite.init().$; | ||
})(); | ||
var mysql = (() => { | ||
return MySql.init().$; | ||
})(); | ||
var postgres = (() => { | ||
return PostgreSql.init().$; | ||
})(); | ||
export { | ||
useFilter, | ||
toStr, | ||
table, | ||
sqlite, | ||
snakeCase, | ||
postgres, | ||
pick, | ||
omit, | ||
nextParam, | ||
mysql, | ||
mergeParams, | ||
isTemplateStrings, | ||
converterFor, | ||
column, | ||
WhereQuery, | ||
UpdateQuery, | ||
Table, | ||
SyncConnection, | ||
SyncDbConnection, | ||
SqliteSchema, | ||
SqliteDialect, | ||
Sqlite, | ||
Sql, | ||
SnakeCaseStrategy, | ||
SelectQuery, | ||
NamingStrategy, | ||
Schema, | ||
PostgreSqlSchema, | ||
PostgreSqlDialect, | ||
PostgreSql, | ||
MySqlSchema, | ||
MySqlDialect, | ||
MySql, | ||
Meta, | ||
Inspect, | ||
DeleteQuery, | ||
DefaultValues, | ||
DefaultStrategy, | ||
DbConnection, | ||
DateTimeConverter, | ||
DataType, | ||
ConnectionBase, | ||
Connection | ||
DataType | ||
}; |
{ | ||
"name": "litdb", | ||
"type": "module", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"main": "./dist/index.cjs", | ||
@@ -16,4 +16,6 @@ "module": "./dist/index.js", | ||
"build": "bun run build.ts", | ||
"minify": "npx -y uglify-js dist/index.js --compress --mangle -o dist/index.min.js", | ||
"compress": "npm run minify && brotli ./dist/index.min.js", | ||
"prepublishOnly": "bun run build", | ||
"release": "bump patch --commit --push --tag && npm publish --access public" | ||
"release": "rm -rf ./dist && npm run build && npm run minify && bump patch --commit --push --tag && npm publish --access public" | ||
}, | ||
@@ -40,7 +42,7 @@ "files": [ | ||
"bun-plugin-dts": "^0.3.0", | ||
"@types/bun": "^1.1.10" | ||
"@types/bun": "^1.1.13" | ||
}, | ||
"dependencies": { | ||
"@types/node": "^22.8.6" | ||
"@types/node": "^22.9.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
128419
6
2865
1
Updated@types/node@^22.9.0