added format command and added trailing commas to reduce conflicts

This commit is contained in:
dswbx
2025-02-26 20:06:03 +01:00
parent 88b5359f1c
commit 7743f71a11
414 changed files with 3622 additions and 3610 deletions

View File

@@ -5,7 +5,7 @@ import {
Type,
parse,
snakeToPascalWithSpaces,
transformObject
transformObject,
} from "core/utils";
import { type Field, PrimaryField, type TActionContext, type TRenderContext } from "../fields";
@@ -16,11 +16,11 @@ export const entityConfigSchema = Type.Object(
name_singular: Type.Optional(Type.String()),
description: Type.Optional(Type.String()),
sort_field: Type.Optional(Type.String({ default: config.data.default_primary_field })),
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" }))
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" })),
},
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type EntityConfig = Static<typeof entityConfigSchema>;
@@ -42,7 +42,7 @@ export type TEntityType = (typeof entityTypes)[number];
*/
export class Entity<
EntityName extends string = string,
Fields extends Record<string, Field<any, any, any>> = Record<string, Field<any, any, any>>
Fields extends Record<string, Field<any, any, any>> = Record<string, Field<any, any, any>>,
> {
readonly #_name!: EntityName;
readonly #_fields!: Fields; // only for types
@@ -99,14 +99,14 @@ export class Entity<
getDefaultSort() {
return {
by: this.config.sort_field ?? "id",
dir: this.config.sort_dir ?? "asc"
dir: this.config.sort_dir ?? "asc",
};
}
getAliasedSelectFrom(
select: string[],
_alias?: string,
context?: TActionContext | TRenderContext
context?: TActionContext | TRenderContext,
): string[] {
const alias = _alias ?? this.name;
return this.getFields()
@@ -114,7 +114,7 @@ export class Entity<
(field) =>
!field.isVirtual() &&
!field.isHidden(context ?? "read") &&
select.includes(field.name)
select.includes(field.name),
)
.map((field) => (alias ? `${alias}.${field.name} as ${field.name}` : field.name));
}
@@ -206,7 +206,7 @@ export class Entity<
options?: {
explain?: boolean;
ignoreUnknown?: boolean;
}
},
): boolean {
if (typeof data !== "object") {
if (options?.explain) {
@@ -224,7 +224,7 @@ export class Entity<
if (unknown_keys.length > 0) {
if (options?.explain) {
throw new Error(
`Entity "${this.name}" data must only contain known keys, unknown: "${unknown_keys}"`
`Entity "${this.name}" data must only contain known keys, unknown: "${unknown_keys}"`,
);
}
}
@@ -265,10 +265,10 @@ export class Entity<
$comment: field.config.description,
$field: field.type,
readOnly: !fillable ? true : undefined,
...field.toJsonSchema()
...field.toJsonSchema(),
};
}),
{ additionalProperties: false }
{ additionalProperties: false },
);
return options?.clean ? JSON.parse(JSON.stringify(schema)) : schema;
@@ -280,7 +280,7 @@ export class Entity<
type: this.type,
//fields: transformObject(this.fields, (field) => field.toJSON()),
fields: Object.fromEntries(this.fields.map((field) => [field.name, field.toJSON()])),
config: this.config
config: this.config,
};
}
}

View File

@@ -5,7 +5,7 @@ import { Connection } from "../connection/Connection";
import {
EntityNotDefinedException,
TransformRetrieveFailedException,
UnableToConnectException
UnableToConnectException,
} from "../errors";
import { MutatorEvents, RepositoryEvents } from "../events";
import type { Field } from "../fields/Field";
@@ -18,7 +18,7 @@ import { type EntityData, Mutator, Repository } from "./index";
type EntitySchema<
TBD extends object = DefaultDB,
E extends Entity | keyof TBD | string = string
E extends Entity | keyof TBD | string = string,
> = E extends Entity<infer Name>
? Name extends keyof TBD
? Name
@@ -42,7 +42,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
connection: Connection,
relations: EntityRelation[] = [],
indices: EntityIndex[] = [],
emgr?: EventManager<any>
emgr?: EventManager<any>,
) {
// add entities & relations
entities.forEach((entity) => this.addEntity(entity));
@@ -114,11 +114,11 @@ export class EntityManager<TBD extends object = DefaultDB> {
entity<Silent extends true | false = false>(
e: Entity | keyof TBD | string,
silent?: Silent
silent?: Silent,
): Silent extends true ? Entity | undefined : Entity {
// make sure to always retrieve by name
const entity = this.entities.find((entity) =>
e instanceof Entity ? entity.name === e.name : entity.name === e
e instanceof Entity ? entity.name === e.name : entity.name === e,
);
if (!entity) {
@@ -177,7 +177,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
if (found) {
throw new Error(
`Relation "${relation.type}" between "${relation.source.entity.name}" ` +
`and "${relation.target.entity.name}" already exists`
`and "${relation.target.entity.name}" already exists`,
);
}
@@ -206,7 +206,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
}
repository<E extends Entity | keyof TBD | string>(
entity: E
entity: E,
): Repository<TBD, EntitySchema<TBD, E>> {
return this.repo(entity);
}
@@ -277,7 +277,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
row[key] = field.transformRetrieve(value as any);
} catch (e: any) {
throw new TransformRetrieveFailedException(
`"${field.type}" field "${key}" on entity "${entity.name}": ${e.message}`
`"${field.type}" field "${key}" on entity "${entity.name}": ${e.message}`,
);
}
}
@@ -293,7 +293,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
entities: Object.fromEntries(this.entities.map((e) => [e.name, e.toJSON()])),
relations: Object.fromEntries(this.relations.all.map((r) => [r.getName(), r.toJSON()])),
//relations: this.relations.all.map((r) => r.toJSON()),
indices: Object.fromEntries(this.indices.map((i) => [i.name, i.toJSON()]))
indices: Object.fromEntries(this.indices.map((i) => [i.name, i.toJSON()])),
};
}
}

View File

@@ -29,7 +29,7 @@ export class Mutator<
TBD extends object = DefaultDB,
TB extends keyof TBD = any,
Output = TBD[TB],
Input = Omit<Output, "id">
Input = Omit<Output, "id">,
> implements EmitsEvents
{
em: EntityManager<TBD>;
@@ -85,7 +85,7 @@ export class Mutator<
`Field "${key}" not found on entity "${entity.name}". Fields: ${entity
.getFillableFields()
.map((f) => f.name)
.join(", ")}`
.join(", ")}`,
);
}
@@ -118,7 +118,7 @@ export class Mutator<
sql,
parameters: [...parameters],
result: result,
data
data,
};
} catch (e) {
// @todo: redact
@@ -139,14 +139,14 @@ export class Mutator<
}
const result = await this.emgr.emit(
new Mutator.Events.MutatorInsertBefore({ entity, data: data as any })
new Mutator.Events.MutatorInsertBefore({ entity, data: data as any }),
);
// if listener returned, take what's returned
const _data = result.returned ? result.params.data : data;
const validatedData = {
...entity.getDefaultObject(),
...(await this.getValidatedData(_data, "create"))
...(await this.getValidatedData(_data, "create")),
};
// check if required fields are present
@@ -182,8 +182,8 @@ export class Mutator<
new Mutator.Events.MutatorUpdateBefore({
entity,
entityId: id,
data
})
data,
}),
);
const _data = result.returned ? result.params.data : data;
@@ -198,7 +198,7 @@ export class Mutator<
const res = await this.single(query);
await this.emgr.emit(
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data })
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }),
);
return res as any;
@@ -220,7 +220,7 @@ export class Mutator<
const res = await this.single(query);
await this.emgr.emit(
new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data })
new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data }),
);
return res as any;
@@ -274,7 +274,7 @@ export class Mutator<
const entity = this.entity;
const qb = this.appendWhere(this.conn.deleteFrom(entity.name), where).returning(
entity.getSelect()
entity.getSelect(),
);
return (await this.many(qb)) as any;
@@ -282,7 +282,7 @@ export class Mutator<
async updateWhere(
data: Partial<Input>,
where?: RepoQuery["where"]
where?: RepoQuery["where"],
): Promise<MutatorResponse<Output[]>> {
const entity = this.entity;
const validatedData = await this.getValidatedData(data, "update");
@@ -304,7 +304,7 @@ export class Mutator<
for (const row of data) {
const validatedData = {
...entity.getDefaultObject(),
...(await this.getValidatedData(row, "create"))
...(await this.getValidatedData(row, "create")),
};
// check if required fields are present

View File

@@ -11,7 +11,7 @@ import {
type EntityData,
type EntityManager,
WhereBuilder,
WithBuilder
WithBuilder,
} from "../index";
import { JoinBuilder } from "./JoinBuilder";
@@ -79,7 +79,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const validated = {
...cloneDeep(defaultQuerySchema),
sort: entity.getDefaultSort(),
select: entity.getSelect()
select: entity.getSelect(),
};
if (!options) return validated;
@@ -101,10 +101,10 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
if (invalid.length > 0) {
throw new InvalidSearchParamsException(
`Invalid select field(s): ${invalid.join(", ")}`
`Invalid select field(s): ${invalid.join(", ")}`,
).context({
entity: entity.name,
valid: validated.select
valid: validated.select,
});
}
@@ -122,7 +122,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const related = this.em.relationOf(entity.name, entry);
if (!related) {
throw new InvalidSearchParamsException(
`JOIN: "${entry}" is not a relation of "${entity.name}"`
`JOIN: "${entry}" is not a relation of "${entity.name}"`,
);
}
@@ -155,7 +155,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
if (invalid.length > 0) {
throw new InvalidSearchParamsException(
`Invalid where field(s): ${invalid.join(", ")}`
`Invalid where field(s): ${invalid.join(", ")}`,
).context({ aliases, entity: entity.name });
}
@@ -189,7 +189,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const [_count, _total, result] = await this.em.connection.batchQuery([
countQuery,
totalQuery,
qb
qb,
]);
//$console.log("result", { _count, _total });
@@ -207,8 +207,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
count: _count[0]?.count ?? 0, // @todo: better graceful method
items: result.length,
time,
query: { sql: compiled.sql, parameters: compiled.parameters }
}
query: { sql: compiled.sql, parameters: compiled.parameters },
},
};
} catch (e) {
if (e instanceof Error) {
@@ -221,10 +221,10 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
protected async single(
qb: RepositoryQB,
options: RepoQuery
options: RepoQuery,
): Promise<RepositoryResponse<EntityData>> {
await this.emgr.emit(
new Repository.Events.RepositoryFindOneBefore({ entity: this.entity, options })
new Repository.Events.RepositoryFindOneBefore({ entity: this.entity, options }),
);
const { data, ...response } = await this.performQuery(qb);
@@ -233,8 +233,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
new Repository.Events.RepositoryFindOneAfter({
entity: this.entity,
options,
data: data[0]!
})
data: data[0]!,
}),
);
return { ...response, data: data[0]! };
@@ -248,7 +248,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
ignore?: (keyof RepoQuery)[];
alias?: string;
defaults?: Pick<RepoQuery, "limit" | "offset">;
}
},
) {
const entity = this.entity;
let qb = _qb ?? (this.conn.selectFrom(entity.name) as RepositoryQB);
@@ -262,7 +262,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const defaults = {
limit: 10,
offset: 0,
...config?.defaults
...config?.defaults,
};
if (!ignore.includes("select") && options.select) {
@@ -295,7 +295,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
private buildQuery(
_options?: Partial<RepoQuery>,
ignore: (keyof RepoQuery)[] = []
ignore: (keyof RepoQuery)[] = [],
): { qb: RepositoryQB; options: RepoQuery } {
const entity = this.entity;
const options = this.getValidOptions(_options);
@@ -305,23 +305,23 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
ignore,
alias: entity.name,
// already done
validate: false
validate: false,
}),
options
options,
};
}
async findId(
id: PrimaryFieldType,
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>,
): Promise<RepositoryResponse<TBD[TB] | undefined>> {
const { qb, options } = this.buildQuery(
{
..._options,
where: { [this.entity.getPrimaryField().name]: id },
limit: 1
limit: 1,
},
["offset", "sort"]
["offset", "sort"],
);
return this.single(qb, options) as any;
@@ -329,12 +329,12 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
async findOne(
where: RepoQuery["where"],
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>,
): Promise<RepositoryResponse<TBD[TB] | undefined>> {
const { qb, options } = this.buildQuery({
..._options,
where,
limit: 1
limit: 1,
});
return this.single(qb, options) as any;
@@ -344,7 +344,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const { qb, options } = this.buildQuery(_options);
await this.emgr.emit(
new Repository.Events.RepositoryFindManyBefore({ entity: this.entity, options })
new Repository.Events.RepositoryFindManyBefore({ entity: this.entity, options }),
);
const res = await this.performQuery(qb);
@@ -353,8 +353,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
new Repository.Events.RepositoryFindManyAfter({
entity: this.entity,
options,
data: res.data
})
data: res.data,
}),
);
return res as any;
@@ -364,7 +364,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
async findManyByReference(
id: PrimaryFieldType,
reference: string,
_options?: Partial<Omit<RepoQuery, "limit" | "offset">>
_options?: Partial<Omit<RepoQuery, "limit" | "offset">>,
): Promise<RepositoryResponse<EntityData>> {
const entity = this.entity;
const listable_relations = this.em.relations.listableRelationsOf(entity);
@@ -372,7 +372,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
if (!relation) {
throw new Error(
`Relation "${reference}" not found or not listable on entity "${entity.name}"`
`Relation "${reference}" not found or not listable on entity "${entity.name}"`,
);
}
@@ -380,7 +380,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
const refQueryOptions = relation.getReferenceQuery(newEntity, id as number, reference);
if (!("where" in refQueryOptions) || Object.keys(refQueryOptions.where as any).length === 0) {
throw new Error(
`Invalid reference query for "${reference}" on entity "${newEntity.name}"`
`Invalid reference query for "${reference}" on entity "${newEntity.name}"`,
);
}
@@ -389,8 +389,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
...refQueryOptions,
where: {
...refQueryOptions.where,
..._options?.where
}
..._options?.where,
},
};
return this.cloneFor(newEntity).findMany(findManyOptions);
@@ -415,7 +415,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
sql: compiled.sql,
parameters: [...compiled.parameters],
result,
count: result[0]?.count ?? 0
count: result[0]?.count ?? 0,
};
}
@@ -441,7 +441,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
sql: compiled.sql,
parameters: [...compiled.parameters],
result,
exists: result[0]!.count > 0
exists: result[0]!.count > 0,
};
}
}

View File

@@ -6,14 +6,14 @@ import {
exp,
isBooleanLike,
isPrimitive,
makeValidator
makeValidator,
} from "core";
import type {
DeleteQueryBuilder,
ExpressionBuilder,
ExpressionWrapper,
SelectQueryBuilder,
UpdateQueryBuilder
UpdateQueryBuilder,
} from "kysely";
type Builder = ExpressionBuilder<any, any>;
@@ -34,58 +34,58 @@ const expressions = [
exp(
"$eq",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), "=", v)
(v, k, eb: Builder) => eb(key(k), "=", v),
),
exp(
"$ne",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), "!=", v)
(v, k, eb: Builder) => eb(key(k), "!=", v),
),
exp(
"$gt",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), ">", v)
(v, k, eb: Builder) => eb(key(k), ">", v),
),
exp(
"$gte",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), ">=", v)
(v, k, eb: Builder) => eb(key(k), ">=", v),
),
exp(
"$lt",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), "<", v)
(v, k, eb: Builder) => eb(key(k), "<", v),
),
exp(
"$lte",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), "<=", v)
(v, k, eb: Builder) => eb(key(k), "<=", v),
),
exp(
"$isnull",
(v: BooleanLike) => isBooleanLike(v),
(v, k, eb: Builder) => eb(key(k), v ? "is" : "is not", null)
(v, k, eb: Builder) => eb(key(k), v ? "is" : "is not", null),
),
exp(
"$in",
(v: any[]) => Array.isArray(v),
(v, k, eb: Builder) => eb(key(k), "in", v)
(v, k, eb: Builder) => eb(key(k), "in", v),
),
exp(
"$notin",
(v: any[]) => Array.isArray(v),
(v, k, eb: Builder) => eb(key(k), "not in", v)
(v, k, eb: Builder) => eb(key(k), "not in", v),
),
exp(
"$between",
(v: [number, number]) => Array.isArray(v) && v.length === 2,
(v, k, eb: Builder) => eb.between(key(k), v[0], v[1])
(v, k, eb: Builder) => eb.between(key(k), v[0], v[1]),
),
exp(
"$like",
(v: Primitive) => isPrimitive(v),
(v, k, eb: Builder) => eb(key(k), "like", String(v).replace(/\*/g, "%"))
)
(v, k, eb: Builder) => eb(key(k), "like", String(v).replace(/\*/g, "%")),
),
];
export type WhereQuery = FilterQuery<typeof expressions>;
@@ -103,7 +103,7 @@ export class WhereBuilder {
const fns = validator.build(query, {
value_is_kv: true,
exp_ctx: eb,
convert: true
convert: true,
});
if (fns.$or.length > 0 && fns.$and.length > 0) {
@@ -124,7 +124,7 @@ export class WhereBuilder {
const { keys } = validator.build(query, {
value_is_kv: true,
exp_ctx: () => null,
convert: true
convert: true,
});
return Array.from(keys);
}

View File

@@ -8,7 +8,7 @@ export class WithBuilder {
em: EntityManager<any>,
qb: RepositoryQB,
entity: Entity,
withs: RepoQuery["with"]
withs: RepoQuery["with"],
) {
if (!withs || !isObject(withs)) {
console.warn(`'withs' undefined or invalid, given: ${JSON.stringify(withs)}`);
@@ -36,8 +36,8 @@ export class WithBuilder {
if (query) {
subQuery = em.repo(other.entity).addOptionsToQueryBuilder(subQuery, query as any, {
ignore: ["with", "join", cardinality === 1 ? "limit" : undefined].filter(
Boolean
) as any
Boolean,
) as any,
});
}
@@ -64,7 +64,7 @@ export class WithBuilder {
const related = em.relationOf(entity, ref);
if (!related) {
throw new InvalidSearchParamsException(
`WITH: "${ref}" is not a relation of "${entity}"`
`WITH: "${ref}" is not a relation of "${entity}"`,
);
}
depth++;