Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

minato

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

minato - npm Package Compare versions

Comparing version 3.0.2 to 3.1.0

src/type.ts

550

lib/index.d.ts

@@ -1,226 +0,3 @@

import { Dict, Intersect, MaybeArray, Awaitable, Extract } from 'cosmokit';
import { Extract, Dict, Awaitable, MaybeArray, Intersect } from 'cosmokit';
import { Context, Logger, Service, Spread } from 'cordis';
export function isEvalExpr(value: any): value is Eval.Expr;
export type Uneval<U, A extends boolean> = U extends number ? Eval.Term<number, A> : U extends string ? Eval.Term<string, A> : U extends boolean ? Eval.Term<boolean, A> : U extends Date ? Eval.Term<Date, A> : U extends RegExp ? Eval.Term<RegExp, A> : any;
export type Eval<U> = U extends Comparable ? U : U extends Eval.Expr<infer T> ? T : never;
declare const kExpr: unique symbol;
declare const kType: unique symbol;
declare const kAggr: unique symbol;
export namespace Eval {
interface Expr<T = any, A extends boolean = boolean> {
[kExpr]: true;
[kType]?: T;
[kAggr]?: A;
}
type Any<A extends boolean = boolean> = Comparable | Expr<any, A>;
type Term<T, A extends boolean = boolean> = T | Expr<T, A>;
type Array<T, A extends boolean = boolean> = Term<T, A>[] | Expr<T[], A>;
type Unary<S, R> = <T extends S, A extends boolean>(x: Term<T, A>) => Expr<R, A>;
type Binary<S, R> = <T extends S, A extends boolean>(x: Term<T, A>, y: Term<T, A>) => Expr<R, A>;
type Multi<S, R> = <T extends S, A extends boolean>(...args: Term<T, A>[]) => Expr<R, A>;
interface Aggr<S, R> {
<T extends S>(value: Term<T, false>): Expr<R, true>;
<T extends S, A extends boolean>(value: Array<T, A>): Expr<R, A>;
}
interface Branch<T, A extends boolean> {
case: Term<boolean, A>;
then: Term<T, A>;
}
interface Static {
<A extends boolean>(key: string, value: any): Eval.Expr<any, A>;
if<T extends Comparable, A extends boolean>(cond: Any<A>, vThen: Term<T, A>, vElse: Term<T, A>): Expr<T, A>;
ifNull<T extends Comparable, A extends boolean>(...args: Term<T, A>[]): Expr<T, A>;
switch<T, A extends boolean>(branches: Branch<T, A>[], vDefault: Term<T, A>): Expr<T, A>;
add: Multi<number, number>;
mul: Multi<number, number>;
multiply: Multi<number, number>;
sub: Binary<number, number>;
subtract: Binary<number, number>;
div: Binary<number, number>;
divide: Binary<number, number>;
mod: Binary<number, number>;
modulo: Binary<number, number>;
abs: Unary<number, number>;
floor: Unary<number, number>;
ceil: Unary<number, number>;
round: Unary<number, number>;
exp: Unary<number, number>;
log<A extends boolean>(x: Term<number, A>, base?: Term<number, A>): Expr<number, A>;
pow: Binary<number, number>;
power: Binary<number, number>;
random(): Expr<number, false>;
eq: Multi<Comparable, boolean>;
ne: Binary<Comparable, boolean>;
gt: Binary<Comparable, boolean>;
ge: Binary<Comparable, boolean>;
gte: Binary<Comparable, boolean>;
lt: Binary<Comparable, boolean>;
le: Binary<Comparable, boolean>;
lte: Binary<Comparable, boolean>;
in<T extends Comparable, A extends boolean>(x: Term<T, A>, array: Array<T, A>): Expr<boolean, A>;
nin<T extends Comparable, A extends boolean>(x: Term<T, A>, array: Array<T, A>): Expr<boolean, A>;
concat: Multi<string, string>;
regex<A extends boolean>(x: Term<string, A>, y: Term<string, A> | Term<RegExp, A>): Expr<boolean, A>;
and: Multi<boolean, boolean>;
or: Multi<boolean, boolean>;
not: Unary<boolean, boolean>;
number: Unary<any, number>;
sum: Aggr<number, number>;
avg: Aggr<number, number>;
max: Aggr<number, number> & Aggr<Date, Date>;
min: Aggr<number, number> & Aggr<Date, Date>;
count(value: Any<false>): Expr<number, true>;
length(value: Any<false>): Expr<number, true>;
size<A extends boolean>(value: (Any | Expr<Any, A>)[] | Expr<Any[], A>): Expr<number, A>;
length<A extends boolean>(value: any[] | Expr<any[], A>): Expr<number, A>;
object<T extends Dict<Expr>>(fields: T): Expr<T, false>;
object<T extends any>(row: Row.Cell<T>): Expr<T, false>;
array<T>(value: Expr<T, false>): Expr<T[], true>;
}
}
export const Eval: Eval.Static;
export { Eval as $ };
type MapUneval<S> = {
[K in keyof S]?: Uneval<S[K], false>;
};
export type Update<T = any> = MapUneval<Flatten<T>>;
export function executeEval(data: any, expr: any): any;
export function executeUpdate(data: any, update: any, ref: string): any;
export type Values<S> = S[keyof S];
export type Keys<O, T = any> = Values<{
[K in keyof O]: O[K] extends T | undefined ? K : never;
}> & string;
export type Atomic = number | string | boolean | bigint | symbol | Date;
export type Indexable = string | number;
export type Comparable = string | number | boolean | Date;
type FlatWrap<S, T, P extends string> = {
[K in P]?: S;
} | (S extends Atomic | T ? never : S extends any[] ? never : string extends keyof S ? never : FlatMap<S, T, `${P}.`>);
type FlatMap<S, T = never, P extends string = ''> = Values<{
[K in keyof S & string as `${P}${K}`]: FlatWrap<S[K], S | T, `${P}${K}`>;
}>;
export type Flatten<S> = Intersect<FlatMap<S>>;
export type Row<S> = {
[K in keyof S]-?: Row.Cell<NonNullable<S[K]>>;
};
export namespace Row {
type Cell<T> = Eval.Expr<T, false> & (T extends Comparable ? {} : Row<T>);
type Computed<S, T> = T | ((row: Row<S>) => T);
}
export function isComparable(value: any): value is Comparable;
export function randomId(): string;
export function makeRegExp(source: string | RegExp): RegExp;
export const Primary: unique symbol;
export type Primary = (string | number) & {
[Primary]: true;
};
export interface Field<T = any> {
type: Field.Type<T>;
length?: number;
nullable?: boolean;
initial?: T;
precision?: number;
scale?: number;
expr?: Eval.Expr;
legacy?: string[];
deprecated?: boolean;
}
export namespace Field {
export const number: Type[];
export const string: Type[];
export const boolean: Type[];
export const date: Type[];
export const object: Type[];
export type Type<T = any> = T extends Primary ? 'primary' : T extends number ? 'integer' | 'unsigned' | 'float' | 'double' | 'decimal' : T extends string ? 'char' | 'string' | 'text' : T extends boolean ? 'boolean' : T extends Date ? 'timestamp' | 'date' | 'time' : T extends unknown[] ? 'list' | 'json' : T extends object ? 'json' : 'expr';
type Shorthand<S extends string> = S | `${S}(${any})`;
type MapField<O = any> = {
[K in keyof O]?: Field<O[K]> | Shorthand<Type<O[K]>> | Selection.Callback<O, O[K]>;
};
export type Extension<O = any> = MapField<Flatten<O>>;
export type Config<O = any> = {
[K in keyof O]?: Field<O[K]>;
};
export function parse(source: string | Field): Field;
}
export namespace Model {
type Migration = (database: Database) => Promise<void>;
interface Config<O = {}> {
callback?: Migration;
autoInc: boolean;
primary: MaybeArray<Keys<O>>;
unique: MaybeArray<Keys<O>>[];
foreign: {
[K in keyof O]?: [string, string];
};
}
}
export interface Model<S> extends Model.Config<S> {
}
export class Model<S = any> {
name: string;
fields: Field.Config<S>;
migrations: Map<Model.Migration, string[]>;
constructor(name: string);
extend(fields: Field.Extension<S>, config?: Partial<Model.Config<S>>): void;
private checkIndex;
resolveValue(key: string, value: any): any;
format(source: object, strict?: boolean, prefix?: string, result?: S): S;
parse(source: object, strict?: boolean, prefix?: string, result?: S): S;
create(data?: {}): S;
}
export namespace Driver {
interface Stats {
size: number;
tables: Dict<TableStats>;
}
interface TableStats {
count: number;
size: number;
}
type Cursor<K extends string = never> = K[] | CursorOptions<K>;
interface CursorOptions<K> {
limit?: number;
offset?: number;
fields?: K[];
sort?: Dict<Direction>;
}
interface WriteResult {
inserted?: number;
matched?: number;
modified?: number;
removed?: number;
}
}
export namespace Driver {
type Constructor<T> = new (ctx: Context, config: T) => Driver<T>;
}
export abstract class Driver<T = any> {
ctx: Context;
config: T;
static inject: string[];
abstract start(): Promise<void>;
abstract stop(): Promise<void>;
abstract drop(table: string): Promise<void>;
abstract dropAll(): Promise<void>;
abstract stats(): Promise<Partial<Driver.Stats>>;
abstract prepare(name: string): Promise<void>;
abstract get(sel: Selection.Immutable, modifier: Modifier): Promise<any>;
abstract eval(sel: Selection.Immutable, expr: Eval.Expr): Promise<any>;
abstract set(sel: Selection.Mutable, data: Update): Promise<Driver.WriteResult>;
abstract remove(sel: Selection.Mutable): Promise<Driver.WriteResult>;
abstract create(sel: Selection.Mutable, data: any): Promise<any>;
abstract upsert(sel: Selection.Mutable, data: any[], keys: string[]): Promise<Driver.WriteResult>;
abstract withTransaction(callback: (driver: this) => Promise<void>): Promise<void>;
database: Database;
logger: Logger;
constructor(ctx: Context, config: T);
model<S = any>(table: string | Selection.Immutable | Dict<string | Selection.Immutable>): Model<S>;
migrate(name: string, hooks: MigrationHooks): Promise<void>;
}
export interface MigrationHooks {
before: (keys: string[]) => boolean;
after: (keys: string[]) => void;
finalize: () => Awaitable<void>;
error: (reason: any) => void;
}
export type Query<T = any> = Query.Expr<Flatten<T>> | Query.Shorthand<Indexable> | Selection.Callback<T, boolean>;

@@ -298,5 +75,3 @@ export namespace Query {

protected resolveField(field: FieldLike<S>): Eval.Expr;
protected resolveFields(fields: string | string[] | Dict<FieldLike<S>>): {
[k: string]: any;
};
protected resolveFields(fields: string | string[] | Dict<FieldLike<S>>): any;
execute(): Promise<T>;

@@ -345,2 +120,304 @@ }

export function executeSort(data: any[], modifier: Modifier, name: string): any[];
export namespace Driver {
interface Stats {
size: number;
tables: Dict<TableStats>;
}
interface TableStats {
count: number;
size: number;
}
type Cursor<K extends string = never> = K[] | CursorOptions<K>;
interface CursorOptions<K> {
limit?: number;
offset?: number;
fields?: K[];
sort?: Dict<Direction>;
}
interface WriteResult {
inserted?: number;
matched?: number;
modified?: number;
removed?: number;
}
interface Transformer<S = any, T = any> {
types: Field.Type<S>[];
dump: (value: S | null) => T | null | void;
load: (value: T | null) => S | null | void;
}
}
export namespace Driver {
type Constructor<T> = new (ctx: Context, config: T) => Driver<T>;
}
export abstract class Driver<T = any> {
ctx: Context;
config: T;
static inject: string[];
abstract start(): Promise<void>;
abstract stop(): Promise<void>;
abstract drop(table: string): Promise<void>;
abstract dropAll(): Promise<void>;
abstract stats(): Promise<Partial<Driver.Stats>>;
abstract prepare(name: string): Promise<void>;
abstract get(sel: Selection.Immutable, modifier: Modifier): Promise<any>;
abstract eval(sel: Selection.Immutable, expr: Eval.Expr): Promise<any>;
abstract set(sel: Selection.Mutable, data: Update): Promise<Driver.WriteResult>;
abstract remove(sel: Selection.Mutable): Promise<Driver.WriteResult>;
abstract create(sel: Selection.Mutable, data: any): Promise<any>;
abstract upsert(sel: Selection.Mutable, data: any[], keys: string[]): Promise<Driver.WriteResult>;
abstract withTransaction(callback: (driver: this) => Promise<void>): Promise<void>;
database: Database;
logger: Logger;
types: Dict<Driver.Transformer>;
constructor(ctx: Context, config: T);
model<S = any>(table: string | Selection.Immutable | Dict<string | Selection.Immutable>): Model<S>;
migrate(name: string, hooks: MigrationHooks): Promise<void>;
define<S, T>(converter: Driver.Transformer<S, T>): void;
}
export interface MigrationHooks {
before: (keys: string[]) => boolean;
after: (keys: string[]) => void;
finalize: () => Awaitable<void>;
error: (reason: any) => void;
}
export const Primary: unique symbol;
export type Primary = (string | number) & {
[Primary]: true;
};
export interface Field<T = any> {
type: Type<T>;
deftype?: Field.Type<T>;
length?: number;
nullable?: boolean;
initial?: T;
precision?: number;
scale?: number;
expr?: Eval.Expr;
legacy?: string[];
deprecated?: boolean;
transformers?: Driver.Transformer[];
}
export namespace Field {
export const number: Type[];
export const string: Type[];
export const boolean: Type[];
export const date: Type[];
export const object: Type[];
export type Type<T = any> = T extends Primary ? 'primary' : T extends number ? 'integer' | 'unsigned' | 'float' | 'double' | 'decimal' : T extends string ? 'char' | 'string' | 'text' : T extends boolean ? 'boolean' : T extends Date ? 'timestamp' | 'date' | 'time' : T extends ArrayBuffer ? 'binary' : T extends unknown[] ? 'list' | 'json' : T extends object ? 'json' : 'expr';
type Shorthand<S extends string> = S | `${S}(${any})`;
export type Object<T = any, N = any> = {
type: 'object';
inner?: Extension<T, N>;
} & Omit<Field<T>, 'type'>;
export type Array<T = any, N = any> = {
type: 'array';
inner?: Literal<T, N> | Definition<T, N> | Transform<T, any, N>;
} & Omit<Field<T[]>, 'type'>;
export type Transform<S = any, T = S, N = any> = {
type: Type<T> | Keys<N, T> | NewType<T> | 'object' | 'array';
dump: (value: S | null) => T | null | void;
load: (value: T | null) => S | null | void;
initial?: S;
} & Omit<Definition<T, N>, 'type' | 'initial'>;
export type Definition<T, N> = (Omit<Field<T>, 'type'> & {
type: Type<T> | Keys<N, T> | NewType<T>;
}) | (T extends object ? Object<T, N> : never) | (T extends (infer I)[] ? Array<I, N> : never);
export type Literal<T, N> = Shorthand<Type<T>> | Keys<N, T> | NewType<T> | (T extends object ? 'object' : never) | (T extends unknown[] ? 'array' : never);
export type Parsable<T = any> = {
type: Type<T> | Field<T>['type'];
} & Omit<Field<T>, 'type'>;
type MapField<O = any, N = any> = {
[K in keyof O]?: Literal<O[K], N> | Definition<O[K], N> | Transform<O[K], any, N>;
};
export type Extension<O = any, N = any> = MapField<Flatten<O>, N>;
const NewType: unique symbol;
export type NewType<T> = string & {
[NewType]: T;
};
export type Config<O = any> = {
[K in keyof O]?: Field<O[K]>;
};
export function parse(source: string | Parsable): Field;
export function getInitial(type: Field.Type, initial?: any): any;
}
export namespace Model {
type Migration = (database: Database) => Promise<void>;
interface Config<O = {}> {
callback?: Migration;
autoInc: boolean;
primary: MaybeArray<Keys<O>>;
unique: MaybeArray<Keys<O>>[];
foreign: {
[K in keyof O]?: [string, string];
};
}
}
export interface Model<S> extends Model.Config<S> {
}
export class Model<S = any> {
name: string;
fields: Field.Config<S>;
migrations: Map<Model.Migration, string[]>;
private type;
constructor(name: string);
extend(fields: Field.Extension<S>, config?: Partial<Model.Config<S>>): void;
private checkIndex;
resolveValue(field: string | Field | Type, value: any): any;
resolveModel(obj: any, model?: Type): any;
format(source: object, strict?: boolean, prefix?: string, result?: S): any;
parse(source: object, strict?: boolean, prefix?: string, result?: S): any;
create(data?: {}): any;
getType(): Type<S>;
getType(key: string): Type | undefined;
}
export interface Type<T = any, N = any> {
[Type.kType]?: true;
type: Field.Type<T> | Keys<N, T> | Field.NewType<T>;
inner?: T extends (infer I)[] ? Type<I, N> : Field.Type<T> extends 'json' ? {
[key in keyof T]: Type<T[key], N>;
} : never;
array?: boolean;
}
export namespace Type {
export const kType: unique symbol;
export const Boolean: Type<boolean>;
export const Number: Type<number>;
export const String: Type<string>;
type Extract<T> = T extends Type<infer I> ? I : T extends Field<infer I> ? I : T extends Field.Type<infer I> ? I : T extends Eval.Term<infer I> ? I : never;
export type Object<T = any> = Type<T>;
export const Object: <T extends unknown>(obj?: T) => Object<{ [K in keyof T]: Extract<T>; }>;
export type Array<T = any> = Type<T[]>;
export const Array: <T>(type?: Type<T>) => Type.Array<T>;
export function fromPrimitive<T>(value: T): Type<T>;
export function fromField<T, N>(field: Type | Field<T> | Field.Type<T> | Keys<N, T> | Field.NewType<T>): Type<T, N>;
export function fromTerm<T>(value: Eval.Term<T>): Type<T>;
export function isType(value: any): value is Type;
export function isArray(type: Type): boolean | undefined;
export function getInner(type?: Type, key?: string): Type | undefined;
}
export function isEvalExpr(value: any): value is Eval.Expr;
export type Uneval<U, A extends boolean> = U extends number ? Eval.Term<number, A> : U extends string ? Eval.Term<string, A> : U extends boolean ? Eval.Term<boolean, A> : U extends Date ? Eval.Term<Date, A> : U extends RegExp ? Eval.Term<RegExp, A> : any;
export type Eval<U> = U extends Comparable ? U : U extends Eval.Expr<infer T> ? T : never;
declare const kExpr: unique symbol;
declare const kType: unique symbol;
declare const kAggr: unique symbol;
export namespace Eval {
interface Expr<T = any, A extends boolean = boolean> {
[kExpr]: true;
[kType]?: T;
[kAggr]?: A;
[Type.kType]?: Type<T>;
}
type Any<A extends boolean = boolean> = Comparable | Expr<any, A>;
type Term<T, A extends boolean = boolean> = T | Expr<T, A>;
type Array<T, A extends boolean = boolean> = Term<T, A>[] | Expr<T[], A>;
type Unary<S, R> = <T extends S, A extends boolean>(x: Term<T, A>) => Expr<R, A>;
type Binary<S, R> = <T extends S, A extends boolean>(x: Term<T, A>, y: Term<T, A>) => Expr<R, A>;
type Multi<S, R> = <T extends S, A extends boolean>(...args: Term<T, A>[]) => Expr<R, A>;
interface Aggr<S, R> {
<T extends S>(value: Term<T, false>): Expr<R, true>;
<T extends S, A extends boolean>(value: Array<T, A>): Expr<R, A>;
}
interface Branch<T, A extends boolean> {
case: Term<boolean, A>;
then: Term<T, A>;
}
interface Static {
<A extends boolean>(key: string, value: any, type: Type): Eval.Expr<any, A>;
if<T extends Comparable, A extends boolean>(cond: Any<A>, vThen: Term<T, A>, vElse: Term<T, A>): Expr<T, A>;
ifNull<T extends Comparable, A extends boolean>(...args: Term<T, A>[]): Expr<T, A>;
switch<T, A extends boolean>(branches: Branch<T, A>[], vDefault: Term<T, A>): Expr<T, A>;
add: Multi<number, number>;
mul: Multi<number, number>;
multiply: Multi<number, number>;
sub: Binary<number, number>;
subtract: Binary<number, number>;
div: Binary<number, number>;
divide: Binary<number, number>;
mod: Binary<number, number>;
modulo: Binary<number, number>;
abs: Unary<number, number>;
floor: Unary<number, number>;
ceil: Unary<number, number>;
round: Unary<number, number>;
exp: Unary<number, number>;
log<A extends boolean>(x: Term<number, A>, base?: Term<number, A>): Expr<number, A>;
pow: Binary<number, number>;
power: Binary<number, number>;
random(): Expr<number, false>;
eq: Multi<Comparable, boolean>;
ne: Binary<Comparable, boolean>;
gt: Binary<Comparable, boolean>;
ge: Binary<Comparable, boolean>;
gte: Binary<Comparable, boolean>;
lt: Binary<Comparable, boolean>;
le: Binary<Comparable, boolean>;
lte: Binary<Comparable, boolean>;
in<T extends Comparable, A extends boolean>(x: Term<T, A>, array: Array<T, A>): Expr<boolean, A>;
nin<T extends Comparable, A extends boolean>(x: Term<T, A>, array: Array<T, A>): Expr<boolean, A>;
concat: Multi<string, string>;
regex<A extends boolean>(x: Term<string, A>, y: Term<string, A> | Term<RegExp, A>): Expr<boolean, A>;
and: Multi<boolean, boolean>;
or: Multi<boolean, boolean>;
not: Unary<boolean, boolean>;
literal<T>(value: T, type?: Field.Type<T> | Field.NewType<T> | string): Expr<T, false>;
number: Unary<any, number>;
sum: Aggr<number, number>;
avg: Aggr<number, number>;
max: Aggr<number, number> & Aggr<Date, Date>;
min: Aggr<number, number> & Aggr<Date, Date>;
count(value: Any<false>): Expr<number, true>;
length(value: Any<false>): Expr<number, true>;
size<A extends boolean>(value: (Any | Expr<Any, A>)[] | Expr<Any[], A>): Expr<number, A>;
length<A extends boolean>(value: any[] | Expr<any[], A>): Expr<number, A>;
object<T extends any>(row: Row.Cell<T>): Expr<T, false>;
object<T extends any>(row: Row<T>): Expr<T, false>;
array<T>(value: Expr<T, false>): Expr<T[], true>;
}
}
export const Eval: Eval.Static;
export { Eval as $ };
type MapUneval<S> = {
[K in keyof S]?: Uneval<S[K], false>;
};
export type Update<T = any> = MapUneval<Flatten<T>>;
export function executeEval(data: any, expr: any): any;
export function executeUpdate(data: any, update: any, ref: string): any;
export type Values<S> = S[keyof S];
export type Keys<O, T = any> = Values<{
[K in keyof O]: O[K] extends T | undefined ? K : never;
}> & string;
export interface AtomicTypes {
Number: number;
String: string;
Boolean: boolean;
BigInt: bigint;
Symbol: symbol;
Date: Date;
RegExp: RegExp;
Function: Function;
ArrayBuffer: ArrayBuffer;
SharedArrayBuffer: SharedArrayBuffer;
}
export type Indexable = string | number;
export type Comparable = string | number | boolean | Date;
type FlatWrap<S, A extends 0[], P extends string> = {
[K in P]?: S;
} | (S extends Values<AtomicTypes> ? never : S extends any[] ? never : string extends keyof S ? never : A extends [0, ...infer R extends 0[]] ? FlatMap<S, R, `${P}.`> : never);
type FlatMap<S, T extends 0[], P extends string = ''> = Values<{
[K in keyof S & string as `${P}${K}`]: FlatWrap<S[K], T, `${P}${K}`>;
}>;
type Sequence<N extends number, A extends 0[] = []> = A['length'] extends N ? A : Sequence<N, [0, ...A]>;
export type Flatten<S, D extends number = 5> = Intersect<FlatMap<S, Sequence<D>>>;
export type Row<S> = {
[K in keyof S]-?: Row.Cell<NonNullable<S[K]>>;
};
export namespace Row {
type Cell<T> = Eval.Expr<T, false> & (T extends Comparable ? {} : Row<T>);
type Computed<S, T> = T | ((row: Row<S>) => T);
}
export function isComparable(value: any): value is Comparable;
export function randomId(): string;
export function makeRegExp(source: string | RegExp): RegExp;
export function unravel(source: object, init?: (value: any) => any): {};
type TableLike<S> = Keys<S> | Selection;

@@ -366,3 +443,3 @@ type TableType<S, T extends TableLike<S>> = T extends Keys<S> ? S[T] : T extends Selection<infer U> ? U : never;

}
export class Database<S = any, C extends Context = Context> extends Service<undefined, C> {
export class Database<S = any, N = any, C extends Context = Context> extends Service<undefined, C> {
static [Service.provide]: string;

@@ -374,2 +451,3 @@ static [Service.immediate]: boolean;

drivers: Record<keyof any, Driver>;
types: Dict<Field.Transform>;
migrating: boolean;

@@ -384,4 +462,8 @@ private prepareTasks;

private prepare;
extend<K extends Keys<S>>(name: K, fields: Field.Extension<S[K]>, config?: Partial<Model.Config<S[K]>>): void;
migrate<K extends Keys<S>>(name: K, fields: Field.Extension<S[K]>, callback: Model.Migration): void;
extend<K extends Keys<S>>(name: K, fields: Field.Extension<S[K], N>, config?: Partial<Model.Config<S[K]>>): void;
private _parseField;
private parseField;
define<K extends Exclude<Keys<N>, Field.Type | 'object' | 'array'>>(name: K, field: Field.Definition<N[K], N> | Field.Transform<N[K], any, N>): K;
define<S>(field: Field.Definition<S, N> | Field.Transform<S, any, N>): Field.NewType<S>;
migrate<K extends Keys<S>>(name: K, fields: Field.Extension<S[K], N>, callback: Model.Migration): void;
select<T>(table: Selection<T>, query?: Query<T>): Selection<T>;

@@ -397,4 +479,4 @@ select<T extends Keys<S>>(table: T, query?: Query<S[T]>): Selection<S[T]>;

upsert<T extends Keys<S>>(table: T, upsert: Row.Computed<S[T], Update<S[T]>[]>, keys?: MaybeArray<Keys<Flatten<S[T]>, Indexable>>): Promise<Driver.WriteResult>;
withTransaction(callback: (database: Database<S>) => Promise<void>): Promise<void>;
withTransaction<T extends Keys<S>>(table: T, callback: (database: Database<S>) => Promise<void>): Promise<void>;
withTransaction(callback: (database: this) => Promise<void>): Promise<void>;
withTransaction<T extends Keys<S>>(table: T, callback: (database: this) => Promise<void>): Promise<void>;
stopAll(): Promise<void>;

@@ -423,3 +505,7 @@ drop<T extends Keys<S>>(table: T): Promise<void>;

}
export interface Tables {
}
export interface Types {
}
export { Logger, Schema, Schema as z } from 'cordis';
export default Database;
{
"name": "minato",
"version": "3.0.2",
"version": "3.1.0",
"description": "Type Driven Database Framework",

@@ -46,4 +46,4 @@ "type": "module",

"cordis": "^3.13.4",
"cosmokit": "^1.5.2"
"cosmokit": "^1.6.2"
}
}

@@ -1,4 +0,4 @@

import { Dict, Intersect, makeArray, MaybeArray, valueMap } from 'cosmokit'
import { Dict, Intersect, makeArray, mapValues, MaybeArray, omit, valueMap } from 'cosmokit'
import { Context, Service, Spread } from 'cordis'
import { Flatten, Indexable, Keys, Row } from './utils.ts'
import { Flatten, Indexable, Keys, randomId, Row, unravel } from './utils.ts'
import { Selection } from './selection.ts'

@@ -9,2 +9,3 @@ import { Field, Model } from './model.ts'

import { Query } from './query.ts'
import { Type } from './type.ts'

@@ -51,3 +52,3 @@ type TableLike<S> = Keys<S> | Selection

export class Database<S = any, C extends Context = Context> extends Service<undefined, C> {
export class Database<S = any, N = any, C extends Context = Context> extends Service<undefined, C> {
static [Service.provide] = 'model'

@@ -58,2 +59,3 @@ static [Service.immediate] = true

public drivers: Record<keyof any, Driver> = Object.create(null)
public types: Dict<Field.Transform> = Object.create(null)
public migrating = false

@@ -96,6 +98,13 @@ private prepareTasks: Dict<Promise<void>> = Object.create(null)

if (!this.stashed.delete(name)) return
await this.getDriver(name)?.prepare(name)
const driver = this.getDriver(name)
if (!driver) return
const { fields } = driver.model(name)
Object.values(fields).forEach(field => field?.transformers?.forEach(x => driver.define(x)))
await driver.prepare(name)
}
extend<K extends Keys<S>>(name: K, fields: Field.Extension<S[K]>, config: Partial<Model.Config<S[K]>> = {}) {
extend<K extends Keys<S>>(name: K, fields: Field.Extension<S[K], N>, config: Partial<Model.Config<S[K]>> = {}) {
let model = this.tables[name]

@@ -106,2 +115,7 @@ if (!model) {

}
Object.entries(fields).forEach(([key, field]: [string, any]) => {
const transformer = []
this.parseField(field, transformer, undefined, value => field = fields[key] = value)
if (typeof field === 'object') field.transformers = transformer
})
model.extend(fields, config)

@@ -112,3 +126,97 @@ this.prepareTasks[name] = this.prepare(name)

migrate<K extends Keys<S>>(name: K, fields: Field.Extension<S[K]>, callback: Model.Migration) {
private _parseField(field: any, transformers: Driver.Transformer[] = [], setInitial?: (value) => void, setField?: (value) => void): Type {
if (field === 'object') {
setInitial?.({})
setField?.({ type: 'json', initial: {} })
return Type.Object()
} else if (field === 'array') {
setInitial?.([])
setField?.({ type: 'json', initial: [] })
return Type.Array()
} else if (typeof field === 'string' && this.types[field]) {
transformers.push({
types: [field as any],
load: this.types[field].load,
dump: this.types[field].dump,
}, ...(this.types[field].transformers ?? []))
setInitial?.(this.types[field].initial)
setField?.({ ...this.types[field], type: field })
return Type.fromField(field)
} else if (typeof field === 'string') {
setInitial?.(Field.getInitial((field as any).split('(')[0]))
setField?.(field)
return Type.fromField(field.split('(')[0])
} else if (typeof field === 'object' && field.type === 'object') {
const inner = field.inner ? unravel(field.inner, value => (value.type = 'object', value.inner ??= {})) : Object.create(null)
const initial = Object.create(null)
const res = Type.Object(mapValues(inner, (x, k) => this.parseField(x, transformers, value => initial[k] = value)))
setInitial?.(Field.getInitial('json', initial))
setField?.({ initial: Field.getInitial('json', initial), ...field, deftype: 'json', type: res })
return res
} else if (typeof field === 'object' && field.type === 'array') {
const res = field.inner ? Type.Array(this.parseField(field.inner, transformers)) : Type.Array()
setInitial?.([])
setField?.({ initial: [], ...field, deftype: 'json', type: res })
return res
} else if (typeof field === 'object' && this.types[field.type]) {
transformers.push({
types: [field.type as any],
load: this.types[field.type].load,
dump: this.types[field.type].dump,
}, ...(this.types[field.type].transformers ?? []))
setInitial?.(field.initial === undefined ? this.types[field.type].initial : field.initial)
setField?.({ initial: this.types[field.type].initial, ...field })
return Type.fromField(field.type)
} else {
setInitial?.(Field.getInitial(field.type, field.initial))
setField?.(field)
return Type.fromField(field.type)
}
}
private parseField(field: any, transformers: Driver.Transformer[] = [], setInitial?: (value) => void, setField?: (value: Field.Parsable) => void): Type {
let midfield
let type = this._parseField(field, transformers, setInitial, (value) => (midfield = value, setField?.(value)))
if (typeof field === 'object' && field.load && field.dump) {
if (type.inner) type = Type.fromField(this.define({ ...omit(midfield, ['load', 'dump']), type } as any))
const name = this.define({ ...field, deftype: midfield.deftype, type: type.type })
transformers.push({
types: [name as any],
load: field.load,
dump: field.dump,
})
// for transform type, intentionally assign a null initial on default
setInitial?.(field.initial)
setField?.({ ...field, deftype: midfield.deftype ?? this.types[type.type]?.deftype ?? type.type, initial: midfield.initial, type: name })
return Type.fromField(name as any)
}
if (typeof midfield === 'object') setField?.({ ...midfield, deftype: midfield.deftype ?? this.types[type.type]?.deftype ?? type?.type })
return type
}
define<K extends Exclude<Keys<N>, Field.Type | 'object' | 'array'>>(name: K, field: Field.Definition<N[K], N> | Field.Transform<N[K], any, N>): K
define<S>(field: Field.Definition<S, N> | Field.Transform<S, any, N>): Field.NewType<S>
define(name: any, field?: any) {
if (typeof name === 'object') {
field = name
name = undefined
}
if (name && this.types[name]) throw new Error(`type "${name}" already defined`)
if (!name) while (this.types[name = '_define_' + randomId()]);
const transformers = []
const type = this._parseField(field, transformers, undefined, value => field = value)
field.transformers = transformers
this[Context.current].effect(() => {
this.types[name] = { ...field }
this.types[name].deftype ??= this.types[field.type]?.deftype ?? type.type as any
return () => delete this.types[name]
})
return name as any
}
migrate<K extends Keys<S>>(name: K, fields: Field.Extension<S[K], N>, callback: Model.Migration) {
this.extend(name, fields, { callback })

@@ -192,4 +300,4 @@ }

async withTransaction(callback: (database: Database<S>) => Promise<void>): Promise<void>
async withTransaction<T extends Keys<S>>(table: T, callback: (database: Database<S>) => Promise<void>): Promise<void>
async withTransaction(callback: (database: this) => Promise<void>): Promise<void>
async withTransaction<T extends Keys<S>>(table: T, callback: (database: this) => Promise<void>): Promise<void>
async withTransaction(arg: any, ...args: any[]) {

@@ -196,0 +304,0 @@ if (this[kTransaction]) throw new Error('nested transactions are not supported')

@@ -5,4 +5,5 @@ import { Awaitable, Dict, valueMap } from 'cosmokit'

import { Direction, Modifier, Selection } from './selection.ts'
import { Model } from './model.ts'
import { Field, Model } from './model.ts'
import { Database } from './database.ts'
import { Type } from './type.ts'

@@ -35,2 +36,8 @@ export namespace Driver {

}
export interface Transformer<S = any, T = any> {
types: Field.Type<S>[]
dump: (value: S | null) => T | null | void
load: (value: T | null) => S | null | void
}
}

@@ -61,2 +68,3 @@

public logger: Logger
public types: Dict<Driver.Transformer> = Object.create(null)

@@ -93,4 +101,4 @@ constructor(public ctx: Context, public config: T) {

const model = new Model('temp')
model.fields = valueMap(table.args[0].fields, (_, key) => ({
type: 'expr',
model.fields = valueMap(table.args[0].fields, (expr, key) => ({
type: Type.fromTerm(expr),
}))

@@ -106,4 +114,4 @@ return model

model.fields[`${key}.${field}`] = {
type: 'expr',
expr: { $: [key, field] } as any,
expr: Eval('', [table[key].ref, field], Type.fromField(submodel.fields[field]!)),
type: Type.fromField(submodel.fields[field]!),
}

@@ -132,2 +140,6 @@ }

}
define<S, T>(converter: Driver.Transformer<S, T>) {
converter.types.forEach(type => this.types[type] = converter)
}
}

@@ -134,0 +146,0 @@

@@ -1,3 +0,5 @@

import { defineProperty, Dict, isNullable, valueMap } from 'cosmokit'
import { defineProperty, isNullable, valueMap } from 'cosmokit'
import { Comparable, Flatten, isComparable, makeRegExp, Row } from './utils.ts'
import { Type } from './type.ts'
import { Field } from './model.ts'

@@ -30,2 +32,3 @@ export function isEvalExpr(value: any): value is Eval.Expr {

[kAggr]?: A
[Type.kType]?: Type<T>
}

@@ -53,3 +56,3 @@

export interface Static {
<A extends boolean>(key: string, value: any): Eval.Expr<any, A>
<A extends boolean>(key: string, value: any, type: Type): Eval.Expr<any, A>

@@ -107,2 +110,3 @@ // univeral

// typecast
literal<T>(value: T, type?: Field.Type<T> | Field.NewType<T> | string): Expr<T, false>
number: Unary<any, number>

@@ -120,4 +124,4 @@

object<T extends Dict<Expr>>(fields: T): Expr<T, false>
object<T extends any>(row: Row.Cell<T>): Expr<T, false>
object<T extends any>(row: Row<T>): Expr<T, false>
array<T>(value: Expr<T, false>): Expr<T[], true>

@@ -127,5 +131,5 @@ }

export const Eval = ((key, value) => defineProperty({ ['$' + key]: value }, kExpr, true)) as Eval.Static
export const Eval = ((key, value, type) => defineProperty(defineProperty({ ['$' + key]: value }, kExpr, true), Type.kType, type)) as Eval.Static
const operators = {} as Record<`$${keyof Eval.Static}`, (args: any, data: any) => any>
const operators = Object.create(null) as Record<`$${keyof Eval.Static}`, (args: any, data: any) => any>

@@ -135,11 +139,14 @@ operators['$'] = getRecursive

type UnaryCallback<T> = T extends (value: infer R) => Eval.Expr<infer S> ? (value: R, data: any[]) => S : never
function unary<K extends keyof Eval.Static>(key: K, callback: UnaryCallback<Eval.Static[K]>): Eval.Static[K] {
function unary<K extends keyof Eval.Static>(key: K, callback: UnaryCallback<Eval.Static[K]>, type: Type | ((...args: any[]) => Type)): Eval.Static[K] {
operators[`$${key}`] = callback
return ((value: any) => Eval(key, value)) as any
return ((value: any) => Eval(key, value, typeof type === 'function' ? type(value) : type)) as any
}
type MultivariateCallback<T> = T extends (...args: infer R) => Eval.Expr<infer S> ? (args: R, data: any) => S : never
function multary<K extends keyof Eval.Static>(key: K, callback: MultivariateCallback<Eval.Static[K]>): Eval.Static[K] {
function multary<K extends keyof Eval.Static>(
key: K, callback: MultivariateCallback<Eval.Static[K]>,
type: Type | ((...args: any[]) => Type),
): Eval.Static[K] {
operators[`$${key}`] = callback
return (...args: any) => Eval(key, args) as any
return (...args: any) => Eval(key, args, typeof type === 'function' ? type(...args) : type) as any
}

@@ -155,6 +162,6 @@

}
return (...args: any) => Eval(key, args) as any
return (...args: any) => Eval(key, args, Type.Boolean) as any
}
Eval.switch = (branches, vDefault) => Eval('switch', { branches, default: vDefault })
Eval.switch = (branches, vDefault) => Eval('switch', { branches, default: vDefault }, Type.fromTerm(branches[0]))
operators.$switch = (args, data) => {

@@ -168,21 +175,22 @@ for (const branch of args.branches) {

// univeral
Eval.if = multary('if', ([cond, vThen, vElse], data) => executeEval(data, cond) ? executeEval(data, vThen) : executeEval(data, vElse))
Eval.ifNull = multary('ifNull', ([value, fallback], data) => executeEval(data, value) ?? executeEval(data, fallback))
Eval.if = multary('if', ([cond, vThen, vElse], data) => executeEval(data, cond) ? executeEval(data, vThen)
: executeEval(data, vElse), (cond, vThen, vElse) => Type.fromTerm(vThen))
Eval.ifNull = multary('ifNull', ([value, fallback], data) => executeEval(data, value) ?? executeEval(data, fallback), (value) => Type.fromTerm(value))
// arithmetic
Eval.add = multary('add', (args, data) => args.reduce<number>((prev, curr) => prev + executeEval(data, curr), 0))
Eval.mul = Eval.multiply = multary('multiply', (args, data) => args.reduce<number>((prev, curr) => prev * executeEval(data, curr), 1))
Eval.sub = Eval.subtract = multary('subtract', ([left, right], data) => executeEval(data, left) - executeEval(data, right))
Eval.div = Eval.divide = multary('divide', ([left, right], data) => executeEval(data, left) / executeEval(data, right))
Eval.mod = Eval.modulo = multary('modulo', ([left, right], data) => executeEval(data, left) % executeEval(data, right))
Eval.add = multary('add', (args, data) => args.reduce<number>((prev, curr) => prev + executeEval(data, curr), 0), Type.Number)
Eval.mul = Eval.multiply = multary('multiply', (args, data) => args.reduce<number>((prev, curr) => prev * executeEval(data, curr), 1), Type.Number)
Eval.sub = Eval.subtract = multary('subtract', ([left, right], data) => executeEval(data, left) - executeEval(data, right), Type.Number)
Eval.div = Eval.divide = multary('divide', ([left, right], data) => executeEval(data, left) / executeEval(data, right), Type.Number)
Eval.mod = Eval.modulo = multary('modulo', ([left, right], data) => executeEval(data, left) % executeEval(data, right), Type.Number)
// mathematic
Eval.abs = unary('abs', (arg, data) => Math.abs(executeEval(data, arg)))
Eval.floor = unary('floor', (arg, data) => Math.floor(executeEval(data, arg)))
Eval.ceil = unary('ceil', (arg, data) => Math.ceil(executeEval(data, arg)))
Eval.round = unary('round', (arg, data) => Math.round(executeEval(data, arg)))
Eval.exp = unary('exp', (arg, data) => Math.exp(executeEval(data, arg)))
Eval.log = multary('log', ([left, right], data) => Math.log(executeEval(data, left)) / Math.log(executeEval(data, right ?? Math.E)))
Eval.pow = Eval.power = multary('power', ([left, right], data) => Math.pow(executeEval(data, left), executeEval(data, right)))
Eval.random = () => Eval('random', {})
Eval.abs = unary('abs', (arg, data) => Math.abs(executeEval(data, arg)), Type.Number)
Eval.floor = unary('floor', (arg, data) => Math.floor(executeEval(data, arg)), Type.Number)
Eval.ceil = unary('ceil', (arg, data) => Math.ceil(executeEval(data, arg)), Type.Number)
Eval.round = unary('round', (arg, data) => Math.round(executeEval(data, arg)), Type.Number)
Eval.exp = unary('exp', (arg, data) => Math.exp(executeEval(data, arg)), Type.Number)
Eval.log = multary('log', ([left, right], data) => Math.log(executeEval(data, left)) / Math.log(executeEval(data, right ?? Math.E)), Type.Number)
Eval.pow = Eval.power = multary('power', ([left, right], data) => Math.pow(executeEval(data, left), executeEval(data, right)), Type.Number)
Eval.random = () => Eval('random', {}, Type.Number)
operators.$random = () => Math.random()

@@ -199,24 +207,33 @@

// element
Eval.in = multary('in', ([value, array], data) => executeEval(data, array).includes(executeEval(data, value)))
Eval.nin = multary('nin', ([value, array], data) => !executeEval(data, array).includes(executeEval(data, value)))
Eval.in = multary('in', ([value, array], data) => executeEval(data, array).includes(executeEval(data, value)), Type.Boolean)
Eval.nin = multary('nin', ([value, array], data) => !executeEval(data, array).includes(executeEval(data, value)), Type.Boolean)
// string
Eval.concat = multary('concat', (args, data) => args.map(arg => executeEval(data, arg)).join(''))
Eval.regex = multary('regex', ([value, regex], data) => makeRegExp(executeEval(data, regex)).test(executeEval(data, value)))
Eval.concat = multary('concat', (args, data) => args.map(arg => executeEval(data, arg)).join(''), Type.String)
Eval.regex = multary('regex', ([value, regex], data) => makeRegExp(executeEval(data, regex)).test(executeEval(data, value)), Type.Boolean)
// logical
Eval.and = multary('and', (args, data) => args.every(arg => executeEval(data, arg)))
Eval.or = multary('or', (args, data) => args.some(arg => executeEval(data, arg)))
Eval.not = unary('not', (value, data) => !executeEval(data, value))
Eval.and = multary('and', (args, data) => args.every(arg => executeEval(data, arg)), Type.Boolean)
Eval.or = multary('or', (args, data) => args.some(arg => executeEval(data, arg)), Type.Boolean)
Eval.not = unary('not', (value, data) => !executeEval(data, value), Type.Boolean)
// typecast
Eval.literal = multary('literal', ([value, type]) => {
if (type) throw new TypeError('literal cast is not supported')
else return value
}, (value, type) => type ? Type.fromField(type) : Type.fromTerm(value))
Eval.number = unary('number', (arg, data) => {
const value = executeEval(data, arg)
return value instanceof Date ? Math.floor(value.valueOf() / 1000) : Number(value)
})
}, Type.Number)
const unwrapAggr = (expr: any) => {
const type = Type.fromTerm(expr)
return Type.getInner(type) ?? type
}
// aggregation
Eval.sum = unary('sum', (expr, table) => Array.isArray(table)
? table.reduce<number>((prev, curr) => prev + executeAggr(expr, curr), 0)
: Array.from<number>(executeEval(table, expr)).reduce((prev, curr) => prev + curr, 0))
: Array.from<number>(executeEval(table, expr)).reduce((prev, curr) => prev + curr, 0), Type.Number)
Eval.avg = unary('avg', (expr, table) => {

@@ -228,31 +245,33 @@ if (Array.isArray(table)) return table.reduce((prev, curr) => prev + executeAggr(expr, curr), 0) / table.length

}
})
}, Type.Number)
Eval.max = unary('max', (expr, table) => Array.isArray(table)
? table.map(data => executeAggr(expr, data)).reduce((x, y) => x > y ? x : y, -Infinity)
: Array.from<number>(executeEval(table, expr)).reduce((x, y) => x > y ? x : y, -Infinity))
: Array.from<number>(executeEval(table, expr)).reduce((x, y) => x > y ? x : y, -Infinity), (expr) => unwrapAggr(expr))
Eval.min = unary('min', (expr, table) => Array.isArray(table)
? table.map(data => executeAggr(expr, data)).reduce((x, y) => x < y ? x : y, Infinity)
: Array.from<number>(executeEval(table, expr)).reduce((x, y) => x < y ? x : y, Infinity))
Eval.count = unary('count', (expr, table) => new Set(table.map(data => executeAggr(expr, data))).size)
: Array.from<number>(executeEval(table, expr)).reduce((x, y) => x < y ? x : y, Infinity), (expr) => unwrapAggr(expr))
Eval.count = unary('count', (expr, table) => new Set(table.map(data => executeAggr(expr, data))).size, Type.Number)
defineProperty(Eval, 'length', unary('length', (expr, table) => Array.isArray(table)
? table.map(data => executeAggr(expr, data)).length
: Array.from(executeEval(table, expr)).length))
: Array.from(executeEval(table, expr)).length, Type.Number))
operators.$object = (field, table) => valueMap(field, value => executeAggr(value, table))
Eval.object = (fields) => {
Eval.object = (fields: any) => {
if (fields.$model) {
const modelFields = Object.keys(fields.$model.fields)
const modelFields: [string, Field][] = Object.entries(fields.$model.fields)
const prefix: string = fields.$prefix
return Eval('object', Object.fromEntries(modelFields
.filter(path => path.startsWith(prefix))
.map(k => [k.slice(prefix.length), fields[k.slice(prefix.length)]]),
))
fields = Object.fromEntries(modelFields
.filter(([, field]) => !field.deprecated)
.filter(([path]) => path.startsWith(prefix))
.map(([k]) => [k.slice(prefix.length), fields[k.slice(prefix.length)]]))
return Eval('object', fields, Type.Object(valueMap(fields, (value) => Type.fromTerm(value))))
}
return Eval('object', fields) as any
return Eval('object', fields, Type.Object(valueMap(fields, (value) => Type.fromTerm(value)))) as any
}
Eval.array = unary('array', (expr, table) => Array.isArray(table)
? table.map(data => executeAggr(expr, data))
: Array.from(executeEval(table, expr)))
: Array.from(executeEval(table, expr)), (expr) => Type.Array(Type.fromTerm(expr)))
Eval.exec = unary('exec', (expr, data) => (expr.driver as any).executeSelection(expr, data))
Eval.exec = unary('exec', (expr, data) => (expr.driver as any).executeSelection(expr, data), (expr) => Type.fromTerm(expr.args[0]))

@@ -259,0 +278,0 @@ export { Eval as $ }

@@ -10,2 +10,3 @@ import { Database } from './database.ts'

export * from './selection.ts'
export * from './type.ts'
export * from './utils.ts'

@@ -24,4 +25,8 @@

export interface Tables {}
export interface Types {}
export { Logger, Schema, Schema as z } from 'cordis'
export default Database

@@ -1,6 +0,7 @@

import { clone, isNullable, makeArray, MaybeArray } from 'cosmokit'
import { Binary, clone, isNullable, makeArray, MaybeArray, valueMap } from 'cosmokit'
import { Database } from './database.ts'
import { Eval, isEvalExpr } from './eval.ts'
import { Selection } from './selection.ts'
import { Flatten, Keys } from './utils.ts'
import { Flatten, Keys, unravel } from './utils.ts'
import { Type } from './type.ts'
import { Driver } from './driver.ts'

@@ -11,3 +12,4 @@ export const Primary = Symbol('Primary')

export interface Field<T = any> {
type: Field.Type<T>
type: Type<T>
deftype?: Field.Type<T>
length?: number

@@ -21,2 +23,3 @@ nullable?: boolean

deprecated?: boolean
transformers?: Driver.Transformer[]
}

@@ -37,2 +40,3 @@

: T extends Date ? 'timestamp' | 'date' | 'time'
: T extends ArrayBuffer ? 'binary'
: T extends unknown[] ? 'list' | 'json'

@@ -44,8 +48,44 @@ : T extends object ? 'json'

type MapField<O = any> = {
[K in keyof O]?: Field<O[K]> | Shorthand<Type<O[K]>> | Selection.Callback<O, O[K]>
export type Object<T = any, N = any> = {
type: 'object'
inner?: Extension<T, N>
} & Omit<Field<T>, 'type'>
export type Array<T = any, N = any> = {
type: 'array'
inner?: Literal<T, N> | Definition<T, N> | Transform<T, any, N>
} & Omit<Field<T[]>, 'type'>
export type Transform<S = any, T = S, N = any> = {
type: Type<T> | Keys<N, T> | NewType<T> | 'object' | 'array'
dump: (value: S | null) => T | null | void
load: (value: T | null) => S | null | void
initial?: S
} & Omit<Definition<T, N>, 'type' | 'initial'>
export type Definition<T, N> =
| (Omit<Field<T>, 'type'> & { type: Type<T> | Keys<N, T> | NewType<T> })
| (T extends object ? Object<T, N> : never)
| (T extends (infer I)[] ? Array<I, N> : never)
export type Literal<T, N> =
| Shorthand<Type<T>>
| Keys<N, T>
| NewType<T>
| (T extends object ? 'object' : never)
| (T extends unknown[] ? 'array' : never)
export type Parsable<T = any> = {
type: Type<T> | Field<T>['type']
} & Omit<Field<T>, 'type'>
type MapField<O = any, N = any> = {
[K in keyof O]?: Literal<O[K], N> | Definition<O[K], N> | Transform<O[K], any, N>
}
export type Extension<O = any> = MapField<Flatten<O>>
export type Extension<O = any, N = any> = MapField<Flatten<O>, N>
const NewType = Symbol('newtype')
export type NewType<T> = string & { [NewType]: T }
export type Config<O = any> = {

@@ -57,5 +97,12 @@ [K in keyof O]?: Field<O[K]>

export function parse(source: string | Field): Field {
if (typeof source === 'function') return { type: 'expr', expr: source }
if (typeof source !== 'string') return { initial: null, ...source }
export function parse(source: string | Parsable): Field {
if (typeof source === 'function') throw new TypeError('view field is not supported')
if (typeof source !== 'string') {
return {
initial: null,
deftype: source.type as any,
...source,
type: Type.fromField(source.type),
}
}

@@ -67,11 +114,6 @@ // parse string definition

const args = (capture[2] || '').split(',')
const field: Field = { type }
const field: Field = { deftype: type, type: Type.fromField(type) }
// set default initial value
if (field.initial === undefined) {
if (number.includes(field.type)) field.initial = 0
if (string.includes(field.type)) field.initial = ''
if (field.type === 'list') field.initial = []
if (field.type === 'json') field.initial = {}
}
if (field.initial === undefined) field.initial = getInitial(type)

@@ -88,2 +130,12 @@ // set length information

}
export function getInitial(type: Field.Type, initial?: any) {
if (initial === undefined) {
if (Field.number.includes(type)) return 0
if (Field.string.includes(type)) return ''
if (type === 'list') return []
if (type === 'json') return {}
}
return initial
}
}

@@ -112,2 +164,4 @@

private type: Type<S> | undefined
constructor(public name: string) {

@@ -136,3 +190,3 @@ this.autoInc = false

if (typeof this.primary === 'string' && this.fields[this.primary]?.type === 'primary') {
if (typeof this.primary === 'string' && this.fields[this.primary]?.deftype === 'primary') {
this.autoInc = true

@@ -154,9 +208,11 @@ }

resolveValue(key: string, value: any) {
resolveValue(field: string | Field | Type, value: any) {
if (isNullable(value)) return value
if (this.fields[key]?.type === 'time') {
if (typeof field === 'string') field = this.fields[field] as Field
if (field) field = Type.fromField(field)
if (field?.type === 'time') {
const date = new Date(0)
date.setHours(value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds())
return date
} else if (this.fields[key]?.type === 'date') {
} else if (field?.type === 'date') {
const date = new Date(value)

@@ -169,2 +225,27 @@ date.setHours(0, 0, 0, 0)

resolveModel(obj: any, model?: Type) {
if (!model) model = this.getType()
if (isNullable(obj) || !model.inner) return obj
if (Type.isArray(model) && Array.isArray(obj)) {
return obj.map(x => this.resolveModel(x, Type.getInner(model)!))
}
const result = {}
for (const key in obj) {
const type = Type.getInner(model, key)
if (!type || isNullable(obj[key])) {
result[key] = obj[key]
} else if (type.type !== 'json') {
result[key] = this.resolveValue(type, obj[key])
} else if (type.inner && Type.isArray(type) && Array.isArray(obj[key])) {
result[key] = obj[key].map(x => this.resolveModel(x, Type.getInner(type)))
} else if (type.inner) {
result[key] = this.resolveModel(obj[key], type)
} else {
result[key] = obj[key]
}
}
return result
}
format(source: object, strict = true, prefix = '', result = {} as S) {

@@ -176,3 +257,3 @@ const fields = Object.keys(this.fields)

if (fields.includes(key)) {
result[key] = this.resolveValue(key, value)
result[key] = value
return

@@ -191,3 +272,3 @@ }

})
return result
return prefix === '' ? this.resolveModel(result) : result
}

@@ -197,2 +278,9 @@

const fields = Object.keys(this.fields)
if (strict && prefix === '') {
// initialize object layout
Object.assign(result as any, unravel(Object.fromEntries(fields
.filter(key => key.includes('.'))
.map(key => [key.slice(0, key.lastIndexOf('.')), {}])),
))
}
for (const key in source) {

@@ -209,8 +297,8 @@ let node = result

if (field) {
node[segments[0]] = this.resolveValue(key, value)
} else if (!value || typeof value !== 'object' || isEvalExpr(value) || Array.isArray(value) || Object.keys(value).length === 0) {
node[segments[0]] = value
} else if (!value || typeof value !== 'object' || isEvalExpr(value) || Array.isArray(value) || Binary.is(value) || Object.keys(value).length === 0) {
if (strict) {
throw new TypeError(`unknown field "${fullKey}" in model ${this.name}`)
} else {
node[segments[0]] = this.resolveValue(key, value)
node[segments[0]] = value
}

@@ -222,3 +310,3 @@ } else {

}
return result
return prefix === '' ? this.resolveModel(result) : result
}

@@ -238,2 +326,9 @@

}
getType(): Type<S>
getType(key: string): Type | undefined
getType(key?: string): Type | undefined {
this.type ??= Type.Object(valueMap(this.fields!, field => Type.fromField(field!))) as any
return key ? Type.getInner(this.type, key) : this.type
}
}

@@ -1,2 +0,2 @@

import { defineProperty, Dict, valueMap } from 'cosmokit'
import { defineProperty, Dict, filterKeys, valueMap } from 'cosmokit'
import { Driver } from './driver.ts'

@@ -7,2 +7,3 @@ import { Eval, executeEval } from './eval.ts'

import { Keys, randomId, Row } from './utils.ts'
import { Type } from './type.ts'

@@ -46,3 +47,25 @@ declare module './eval.ts' {

if (typeof key === 'symbol' || key in target || key.startsWith('$')) return Reflect.get(target, key)
return createRow(ref, Eval('', [ref, `${prefix}${key}`]), `${prefix}${key}.`, model)
let type: Type
const field = model?.fields[prefix + key as string]
if (Type.getInner(expr?.[Type.kType], key)) {
// type may conatins object layout
type = Type.getInner(expr?.[Type.kType], key)!
} else if (field) {
type = Type.fromField(field)
} else if (Object.keys(model?.fields!).some(k => k.startsWith(`${prefix}${key}.`))) {
type = Type.Object(Object.fromEntries(Object.entries(model?.fields!)
.filter(([k]) => k.startsWith(`${prefix}${key}`))
.map(([k, field]) => [k.slice(prefix.length + key.length + 1), Type.fromField(field!)])))
} else {
// unknown field inside json
type = Type.fromField('expr')
}
const row = createRow(ref, Eval('', [ref, `${prefix}${key}`], type), `${prefix}${key}.`, model)
if (Object.keys(model?.fields!).some(k => k.startsWith(`${prefix}${key}.`))) {
return createRow(ref, Eval.object(row), `${prefix}${key}.`, model)
} else {
return row
}
},

@@ -92,7 +115,10 @@ })

const modelFields = Object.keys(this.model.fields)
const keys = fields.flatMap((key) => {
if (this.model.fields[key]) return key
return modelFields.filter(path => path.startsWith(key + '.'))
const entries = fields.flatMap((key) => {
if (this.model.fields[key]) return [[key, this.row[key]]]
else if (modelFields.some(path => path.startsWith(key + '.'))) {
return modelFields.filter(path => path.startsWith(key + '.')).map(path => [path, this.row[path]])
}
return [[key, key.split('.').reduce((row, k) => row[k], this.row)]]
})
return Object.fromEntries(keys.map(key => [key, this.row[key]]))
return Object.fromEntries(entries)
} else {

@@ -224,3 +250,5 @@ return valueMap(fields, field => this.resolveField(field))

if (!callback) callback = (row) => Eval.array(Eval.object(row))
return Eval('exec', selection._action('eval', this.resolveField(callback)))
const expr = this.resolveField(callback)
if (expr['$']) defineProperty(expr, Type.kType, Type.Array(Type.fromTerm(expr)))
return Eval.exec(selection._action('eval', expr))
}

@@ -248,2 +276,7 @@

}
if (cursor.fields) {
return super.execute().then(
rows => rows.map(row => filterKeys(row as any, key => (cursor.fields as string[]).some(k => k === key || k.startsWith(`${key}.`)))),
)
}
return super.execute()

@@ -250,0 +283,0 @@ }

@@ -10,21 +10,37 @@ import { Intersect } from 'cosmokit'

export type Atomic = number | string | boolean | bigint | symbol | Date
export interface AtomicTypes {
Number: number
String: string
Boolean: boolean
BigInt: bigint
Symbol: symbol
Date: Date
RegExp: RegExp
Function: Function
ArrayBuffer: ArrayBuffer
SharedArrayBuffer: SharedArrayBuffer
}
export type Indexable = string | number
export type Comparable = string | number | boolean | Date
type FlatWrap<S, T, P extends string> = { [K in P]?: S }
// rule out atomic / recursive types
| (S extends Atomic | T ? never
type FlatWrap<S, A extends 0[], P extends string> = { [K in P]?: S }
// rule out atomic types
| (S extends Values<AtomicTypes> ? never
// rule out array types
: S extends any[] ? never
// check recursion depth
// rule out dict / infinite types
: string extends keyof S ? never
: FlatMap<S, T, `${P}.`>)
: A extends [0, ...infer R extends 0[]] ? FlatMap<S, R, `${P}.`>
: never)
type FlatMap<S, T = never, P extends string = ''> = Values<{
[K in keyof S & string as `${P}${K}`]: FlatWrap<S[K], S | T, `${P}${K}`>
type FlatMap<S, T extends 0[], P extends string = ''> = Values<{
[K in keyof S & string as `${P}${K}`]: FlatWrap<S[K], T, `${P}${K}`>
}>
export type Flatten<S> = Intersect<FlatMap<S>>
type Sequence<N extends number, A extends 0[] = []> = A['length'] extends N ? A : Sequence<N, [0, ...A]>
export type Flatten<S, D extends number = 5> = Intersect<FlatMap<S, Sequence<D>>>
export type Row<S> = {

@@ -55,1 +71,16 @@ [K in keyof S]-?: Row.Cell<NonNullable<S[K]>>

}
export function unravel(source: object, init?: (value) => any) {
const result = {}
for (const key in source) {
let node = result
const segments = key.split('.').reverse()
for (let index = segments.length - 1; index > 0; index--) {
const segment = segments[index]
node = node[segment] ??= {}
if (init) node = init(node)
}
node[segments[0]] = source[key]
}
return result
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc