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 {
EntityIndex,
type EntityManager,
constructEntity,
constructRelation
constructRelation,
} from "data";
import { Module } from "modules/Module";
import { DataController } from "./api/DataController";
@@ -16,7 +16,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
const {
entities: _entities = {},
relations: _relations = {},
indices: _indices = {}
indices: _indices = {},
} = this.config;
this.ctx.logger.context("AppData").log("building with entities", Object.keys(_entities));
@@ -33,7 +33,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
};
const relations = transformObject(_relations, (relation) =>
constructRelation(relation, _entity)
constructRelation(relation, _entity),
);
const indices = transformObject(_indices, (index, name) => {
@@ -56,7 +56,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
this.ctx.server.route(
this.basepath,
new DataController(this.ctx, this.config).getController()
new DataController(this.ctx, this.config).getController(),
);
this.ctx.guard.registerPermissions(Object.values(DataPermissions));
@@ -84,7 +84,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
override toJSON(secrets?: boolean): AppDataConfig {
return {
...this.config,
...this.em.toJSON()
...this.em.toJSON(),
};
}
}

View File

@@ -13,25 +13,25 @@ export class DataApi extends ModuleApi<DataApiOptions> {
basepath: "/api/data",
queryLengthLimit: 1000,
defaultQuery: {
limit: 10
}
limit: 10,
},
};
}
readOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
entity: E,
id: PrimaryFieldType,
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {}
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {},
) {
return this.get<Pick<RepositoryResponse<Data>, "meta" | "data">>(
["entity", entity as any, id],
query
query,
);
}
readMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
entity: E,
query: RepoQueryIn = {}
query: RepoQueryIn = {},
) {
type T = Pick<RepositoryResponse<Data[]>, "meta" | "data">;
@@ -48,17 +48,17 @@ export class DataApi extends ModuleApi<DataApiOptions> {
readManyByReference<
E extends keyof DB | string,
R extends keyof DB | string,
Data = R extends keyof DB ? DB[R] : EntityData
Data = R extends keyof DB ? DB[R] : EntityData,
>(entity: E, id: PrimaryFieldType, reference: R, query: RepoQueryIn = {}) {
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
["entity", entity as any, id, reference],
query ?? this.options.defaultQuery
query ?? this.options.defaultQuery,
);
}
createOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
entity: E,
input: Omit<Data, "id">
input: Omit<Data, "id">,
) {
return this.post<RepositoryResponse<Data>>(["entity", entity as any], input);
}
@@ -66,14 +66,14 @@ export class DataApi extends ModuleApi<DataApiOptions> {
updateOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
entity: E,
id: PrimaryFieldType,
input: Partial<Omit<Data, "id">>
input: Partial<Omit<Data, "id">>,
) {
return this.patch<RepositoryResponse<Data>>(["entity", entity as any, id], input);
}
deleteOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
entity: E,
id: PrimaryFieldType
id: PrimaryFieldType,
) {
return this.delete<RepositoryResponse<Data>>(["entity", entity as any, id]);
}
@@ -81,7 +81,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
count<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"] = {}) {
return this.post<RepositoryResponse<{ entity: E; count: number }>>(
["entity", entity as any, "fn", "count"],
where
where,
);
}
}

View File

@@ -7,7 +7,7 @@ import {
type MutatorResponse,
type RepoQuery,
type RepositoryResponse,
querySchema
querySchema,
} from "data";
import type { Handler } from "hono/types";
import type { ModuleBuildContext } from "modules";
@@ -18,7 +18,7 @@ import type { AppDataConfig } from "../data-schema";
export class DataController extends Controller {
constructor(
private readonly ctx: ModuleBuildContext,
private readonly config: AppDataConfig
private readonly config: AppDataConfig,
) {
super();
}
@@ -32,7 +32,7 @@ export class DataController extends Controller {
}
repoResult<T extends RepositoryResponse<any> = RepositoryResponse>(
res: T
res: T,
): Pick<T, "meta" | "data"> {
let meta: Partial<RepositoryResponse["meta"]> = {};
@@ -48,7 +48,7 @@ export class DataController extends Controller {
//return objectCleanEmpty(template) as any;
// filter empty
return Object.fromEntries(
Object.entries(template).filter(([_, v]) => typeof v !== "undefined" && v !== null)
Object.entries(template).filter(([_, v]) => typeof v !== "undefined" && v !== null),
) as any;
}
@@ -91,7 +91,7 @@ export class DataController extends Controller {
handler("data info", (c) => {
// sample implementation
return c.json(this.em.toJSON());
})
}),
);
// sync endpoint
@@ -103,7 +103,7 @@ export class DataController extends Controller {
//console.log("tables", tables);
const changes = await this.em.schema().sync({
force,
drop
drop,
});
return c.json({ tables: tables.map((t) => t.name), changes });
});
@@ -118,14 +118,14 @@ export class DataController extends Controller {
this.em.entities.map((e) => [
e.name,
{
$ref: `${this.config.basepath}/schemas/${e.name}`
}
])
$ref: `${this.config.basepath}/schemas/${e.name}`,
},
]),
);
return c.json({
$schema: "https://json-schema.org/draft/2020-12/schema",
$id,
properties: schemas
properties: schemas,
});
});
@@ -137,8 +137,8 @@ export class DataController extends Controller {
"param",
Type.Object({
entity: Type.String(),
context: Type.Optional(StringEnum(["create", "update"]))
})
context: Type.Optional(StringEnum(["create", "update"])),
}),
),
async (c) => {
//console.log("request", c.req.raw);
@@ -157,9 +157,9 @@ export class DataController extends Controller {
$id,
title: _entity.label,
$comment: _entity.config.description,
...schema
...schema,
});
}
},
);
// entity endpoints
@@ -178,7 +178,7 @@ export class DataController extends Controller {
const $rels = (r: any) =>
r.map((r: any) => ({
entity: r.other(_entity).entity.name,
ref: r.other(_entity).reference
ref: r.other(_entity).reference,
}));
return c.json({
@@ -188,8 +188,8 @@ export class DataController extends Controller {
all: $rels(this.em.relations.relationsOf(_entity)),
listable: $rels(this.em.relations.listableRelationsOf(_entity)),
source: $rels(this.em.relations.sourceRelationsOf(_entity)),
target: $rels(this.em.relations.targetRelationsOf(_entity))
}
target: $rels(this.em.relations.targetRelationsOf(_entity)),
},
});
});
@@ -222,7 +222,7 @@ export class DataController extends Controller {
const where = (await c.req.json()) as any;
const result = await this.em.repository(entity).count(where);
return c.json({ entity, count: result.count });
}
},
);
// fn: exists
@@ -239,7 +239,7 @@ export class DataController extends Controller {
const where = c.req.json() as any;
const result = await this.em.repository(entity).exists(where);
return c.json({ entity, exists: result.exists });
}
},
);
/**
@@ -263,7 +263,7 @@ export class DataController extends Controller {
const result = await this.em.repository(entity).findMany(options);
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
}
},
);
// read one
@@ -274,8 +274,8 @@ export class DataController extends Controller {
"param",
Type.Object({
entity: Type.String(),
id: tbNumber
})
id: tbNumber,
}),
),
tb("query", querySchema),
async (c) => {
@@ -287,7 +287,7 @@ export class DataController extends Controller {
const result = await this.em.repository(entity).findId(Number(id), options);
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
}
},
);
// read many by reference
@@ -299,8 +299,8 @@ export class DataController extends Controller {
Type.Object({
entity: Type.String(),
id: tbNumber,
reference: Type.String()
})
reference: Type.String(),
}),
),
tb("query", querySchema),
async (c) => {
@@ -315,7 +315,7 @@ export class DataController extends Controller {
.findManyByReference(Number(id), reference, options);
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
}
},
);
// func query
@@ -334,7 +334,7 @@ export class DataController extends Controller {
const result = await this.em.repository(entity).findMany(options);
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
}
},
);
/**
@@ -354,7 +354,7 @@ export class DataController extends Controller {
const result = await this.em.mutator(entity).insertOne(body);
return c.json(this.mutatorResult(result), 201);
}
},
);
// update many
@@ -366,8 +366,8 @@ export class DataController extends Controller {
"json",
Type.Object({
update: Type.Object({}),
where: querySchema.properties.where
})
where: querySchema.properties.where,
}),
),
async (c) => {
const { entity } = c.req.param();
@@ -381,7 +381,7 @@ export class DataController extends Controller {
const result = await this.em.mutator(entity).updateWhere(update, where);
return c.json(this.mutatorResult(result));
}
},
);
// update one
@@ -398,7 +398,7 @@ export class DataController extends Controller {
const result = await this.em.mutator(entity).updateOne(Number(id), body);
return c.json(this.mutatorResult(result));
}
},
);
// delete one
@@ -414,7 +414,7 @@ export class DataController extends Controller {
const result = await this.em.mutator(entity).deleteOne(Number(id));
return c.json(this.mutatorResult(result));
}
},
);
// delete many
@@ -432,7 +432,7 @@ export class DataController extends Controller {
const result = await this.em.mutator(entity).deleteWhere(where);
return c.json(this.mutatorResult(result));
}
},
);
return hono;

View File

@@ -8,7 +8,7 @@ import {
type SelectQueryBuilder,
type SelectQueryNode,
type Simplify,
sql
sql,
} from "kysely";
export type QB = SelectQueryBuilder<any, any, any>;
@@ -33,7 +33,7 @@ export type DbFunctions = {
jsonObjectFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<O> | null>;
jsonArrayFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<O>[]>;
jsonBuildObject<O extends Record<string, Expression<unknown>>>(
obj: O
obj: O,
): RawBuilder<
Simplify<{
[K in keyof O]: O[K] extends Expression<infer V> ? V : never;
@@ -49,7 +49,7 @@ export abstract class Connection<DB = any> {
constructor(
kysely: Kysely<DB>,
public fn: Partial<DbFunctions> = {},
protected plugins: KyselyPlugin[] = []
protected plugins: KyselyPlugin[] = [],
) {
this.kysely = kysely;
this[CONN_SYMBOL] = true;
@@ -84,7 +84,7 @@ export abstract class Connection<DB = any> {
}
protected async batch<Queries extends QB[]>(
queries: [...Queries]
queries: [...Queries],
): Promise<{
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
}> {
@@ -92,7 +92,7 @@ export abstract class Connection<DB = any> {
}
async batchQuery<Queries extends QB[]>(
queries: [...Queries]
queries: [...Queries],
): Promise<{
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
}> {

View File

@@ -15,7 +15,7 @@ export type LibSqlCredentials = Config & {
class CustomLibsqlDialect extends LibsqlDialect {
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
return new SqliteIntrospector(db, {
excludeTables: ["libsql_wasm_func_table"]
excludeTables: ["libsql_wasm_func_table"],
});
}
}
@@ -44,7 +44,7 @@ export class LibsqlConnection extends SqliteConnection {
const kysely = new Kysely({
// @ts-expect-error libsql has type issues
dialect: new CustomLibsqlDialect({ client }),
plugins
plugins,
});
super(kysely, {}, plugins);
@@ -64,7 +64,7 @@ export class LibsqlConnection extends SqliteConnection {
}
protected override async batch<Queries extends QB[]>(
queries: [...Queries]
queries: [...Queries],
): Promise<{
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
}> {
@@ -72,7 +72,7 @@ export class LibsqlConnection extends SqliteConnection {
const compiled = q.compile();
return {
sql: compiled.sql,
args: compiled.parameters as any[]
args: compiled.parameters as any[],
};
});

View File

@@ -10,9 +10,9 @@ export class SqliteConnection extends Connection {
...fn,
jsonArrayFrom,
jsonObjectFrom,
jsonBuildObject
jsonBuildObject,
},
plugins
plugins,
);
}

View File

@@ -5,7 +5,7 @@ import type {
ExpressionBuilder,
Kysely,
SchemaMetadata,
TableMetadata
TableMetadata,
} from "kysely";
import { DEFAULT_MIGRATION_LOCK_TABLE, DEFAULT_MIGRATION_TABLE, sql } from "kysely";
import type { ConnectionIntrospector, IndexMetadata } from "./Connection";
@@ -62,7 +62,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
seqno: number;
cid: number;
name: string;
}>`pragma_index_info(${index})`.as("index_info")
}>`pragma_index_info(${index})`.as("index_info"),
)
.select(["seqno", "cid", "name"])
.orderBy("cid")
@@ -74,8 +74,8 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
isUnique: isUnique,
columns: columns.map((col) => ({
name: col.name,
order: col.seqno
}))
order: col.seqno,
})),
};
}
@@ -87,7 +87,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
}
async getTables(
options: DatabaseMetadataOptions = { withInternalKyselyTables: false }
options: DatabaseMetadataOptions = { withInternalKyselyTables: false },
): Promise<TableMetadata[]> {
let query = this.#db
.selectFrom("sqlite_master")
@@ -99,7 +99,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
if (!options.withInternalKyselyTables) {
query = query.where(
this.excludeTables([DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE])
this.excludeTables([DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE]),
);
}
if (this._excludeTables.length > 0) {
@@ -112,7 +112,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
async getMetadata(options?: DatabaseMetadataOptions): Promise<DatabaseMetadata> {
return {
tables: await this.getTables(options)
tables: await this.getTables(options),
};
}
@@ -142,7 +142,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
type: string;
notnull: 0 | 1;
dflt_value: any;
}>`pragma_table_info(${table})`.as("table_info")
}>`pragma_table_info(${table})`.as("table_info"),
)
.select(["name", "type", "notnull", "dflt_value"])
.orderBy("cid")
@@ -157,8 +157,8 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
isNullable: !col.notnull,
isAutoIncrementing: col.name === autoIncrementCol,
hasDefaultValue: col.dflt_value != null,
comment: undefined
}))
comment: undefined,
})),
};
}
}

View File

@@ -6,7 +6,7 @@ import { SqliteIntrospector } from "./SqliteIntrospector";
class CustomSqliteDialect extends SqliteDialect {
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
return new SqliteIntrospector(db, {
excludeTables: ["test_table"]
excludeTables: ["test_table"],
});
}
}
@@ -16,7 +16,7 @@ export class SqliteLocalConnection extends SqliteConnection {
const plugins = [new ParseJSONResultsPlugin()];
const kysely = new Kysely({
dialect: new CustomSqliteDialect({ database }),
plugins
plugins,
//log: ["query"],
});

View File

@@ -4,14 +4,14 @@ import {
RelationClassMap,
RelationFieldClassMap,
entityConfigSchema,
entityTypes
entityTypes,
} from "data";
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
export const FIELDS = {
...FieldClassMap,
...RelationFieldClassMap,
media: { schema: mediaFieldConfigSchema, field: MediaField }
media: { schema: mediaFieldConfigSchema, field: MediaField },
};
export type FieldType = keyof typeof FIELDS;
@@ -21,11 +21,11 @@ export const fieldsSchemaObject = objectTransform(FIELDS, (field, name) => {
return Type.Object(
{
type: Type.Const(name, { default: name, readOnly: true }),
config: Type.Optional(field.schema)
config: Type.Optional(field.schema),
},
{
title: name
}
title: name,
},
);
});
export const fieldsSchema = Type.Union(Object.values(fieldsSchemaObject));
@@ -37,7 +37,7 @@ export const entitiesSchema = Type.Object({
//name: Type.String(),
type: Type.Optional(Type.String({ enum: entityTypes, default: "regular", readOnly: true })),
config: Type.Optional(entityConfigSchema),
fields: Type.Optional(entityFields)
fields: Type.Optional(entityFields),
});
export type TAppDataEntity = Static<typeof entitiesSchema>;
@@ -47,11 +47,11 @@ export const relationsSchema = Object.entries(RelationClassMap).map(([name, rela
type: Type.Const(name, { default: name, readOnly: true }),
source: Type.String(),
target: Type.String(),
config: Type.Optional(relationClass.schema)
config: Type.Optional(relationClass.schema),
},
{
title: name
}
title: name,
},
);
});
export type TAppDataRelation = Static<(typeof relationsSchema)[number]>;
@@ -61,11 +61,11 @@ export const indicesSchema = Type.Object(
entity: Type.String(),
fields: Type.Array(Type.String(), { minItems: 1 }),
//name: Type.Optional(Type.String()),
unique: Type.Optional(Type.Boolean({ default: false }))
unique: Type.Optional(Type.Boolean({ default: false })),
},
{
additionalProperties: false
}
additionalProperties: false,
},
);
export const dataConfigSchema = Type.Object(
@@ -73,11 +73,11 @@ export const dataConfigSchema = Type.Object(
basepath: Type.Optional(Type.String({ default: "/api/data" })),
entities: Type.Optional(StringRecord(entitiesSchema, { default: {} })),
relations: Type.Optional(StringRecord(Type.Union(relationsSchema), { default: {} })),
indices: Type.Optional(StringRecord(indicesSchema, { default: {} }))
indices: Type.Optional(StringRecord(indicesSchema, { default: {} })),
},
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type AppDataConfig = Static<typeof dataConfigSchema>;

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++;

View File

@@ -42,11 +42,11 @@ export class InvalidFieldConfigException extends Exception {
constructor(
field: Field<any, any, any>,
public given: any,
error: TypeInvalidError
error: TypeInvalidError,
) {
console.error("InvalidFieldConfigException", {
given,
error: error.firstToString()
error: error.firstToString(),
});
super(`Invalid Field config given for field "${field.name}": ${error.firstToString()}`);
}
@@ -71,7 +71,7 @@ export class EntityNotFoundException extends Exception {
constructor(entity: Entity | string, id: any) {
super(
`Entity "${typeof entity !== "string" ? entity.name : entity}" with id "${id}" not found`
`Entity "${typeof entity !== "string" ? entity.name : entity}" with id "${id}" not found`,
);
}
}

View File

@@ -14,7 +14,7 @@ export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityDat
return this.clone({
entity,
data
data,
});
}
}
@@ -40,7 +40,7 @@ export class MutatorUpdateBefore extends Event<
return this.clone({
...rest,
entity,
data
data,
});
}
}
@@ -68,7 +68,7 @@ export const MutatorEvents = {
MutatorUpdateBefore,
MutatorUpdateAfter,
MutatorDeleteBefore,
MutatorDeleteAfter
MutatorDeleteAfter,
};
export class RepositoryFindOneBefore extends Event<{ entity: Entity; options: RepoQuery }> {
@@ -98,5 +98,5 @@ export const RepositoryEvents = {
RepositoryFindOneBefore,
RepositoryFindOneAfter,
RepositoryFindManyBefore,
RepositoryFindManyAfter
RepositoryFindManyAfter,
};

View File

@@ -5,9 +5,9 @@ import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema
export const booleanFieldConfigSchema = Type.Composite([
Type.Object({
default_value: Type.Optional(Type.Boolean({ default: false }))
default_value: Type.Optional(Type.Boolean({ default: false })),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
]);
export type BooleanFieldConfig = Static<typeof booleanFieldConfigSchema>;
@@ -40,7 +40,7 @@ export class BooleanField<Required extends true | false = false> extends Field<
override getHtmlConfig() {
return {
...super.getHtmlConfig(),
element: "boolean"
element: "boolean",
};
}
@@ -64,7 +64,7 @@ export class BooleanField<Required extends true | false = false> extends Field<
override async transformPersist(
val: unknown,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<boolean | undefined> {
const value = await super.transformPersist(val, em, context);
if (this.nullish(value)) {

View File

@@ -9,13 +9,13 @@ export const dateFieldConfigSchema = Type.Composite(
type: StringEnum(["date", "datetime", "week"] as const, { default: "date" }),
timezone: Type.Optional(Type.String()),
min_date: Type.Optional(Type.String()),
max_date: Type.Optional(Type.String())
max_date: Type.Optional(Type.String()),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type DateFieldConfig = Static<typeof dateFieldConfigSchema>;
@@ -43,8 +43,8 @@ export class DateField<Required extends true | false = false> extends Field<
...super.getHtmlConfig(),
element: "date",
props: {
type: htmlType
}
type: htmlType,
},
};
}
@@ -53,7 +53,7 @@ export class DateField<Required extends true | false = false> extends Field<
if (this.config.type === "week" && value.includes("-W")) {
const [year, week] = value.split("-W").map((n) => Number.parseInt(n, 10)) as [
number,
number
number,
];
//console.log({ year, week });
// @ts-ignore causes errors on build?
@@ -129,7 +129,7 @@ export class DateField<Required extends true | false = false> extends Field<
override async transformPersist(
_value: any,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<string | undefined> {
const value = await super.transformPersist(_value, em, context);
if (this.nullish(value)) return value;

View File

@@ -12,9 +12,9 @@ export const enumFieldConfigSchema = Type.Composite(
Type.Object(
{
type: Const("strings"),
values: Type.Array(Type.String())
values: Type.Array(Type.String()),
},
{ title: "Strings" }
{ title: "Strings" },
),
Type.Object(
{
@@ -22,23 +22,23 @@ export const enumFieldConfigSchema = Type.Composite(
values: Type.Array(
Type.Object({
label: Type.String(),
value: Type.String()
})
)
value: Type.String(),
}),
),
},
{
title: "Objects",
additionalProperties: false
}
)
])
)
additionalProperties: false,
},
),
]),
),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type EnumFieldConfig = Static<typeof enumFieldConfigSchema>;
@@ -123,7 +123,7 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
override async transformPersist(
_value: any,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<string | undefined> {
const value = await super.transformPersist(_value, em, context);
if (this.nullish(value)) return value;
@@ -132,7 +132,7 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
throw new TransformPersistFailedException(
`Field "${this.name}" must be one of the following values: ${this.getOptions()
.map((o) => o.value)
.join(", ")}`
.join(", ")}`,
);
}
@@ -146,8 +146,8 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
[];
return this.toSchemaWrapIfRequired(
StringEnum(values, {
default: this.getDefault()
})
default: this.getDefault(),
}),
);
}
}

View File

@@ -5,7 +5,7 @@ import {
Type,
TypeInvalidError,
parse,
snakeToPascalWithSpaces
snakeToPascalWithSpaces,
} from "core/utils";
import type { ColumnBuilderCallback, ColumnDataType, ColumnDefinitionBuilder } from "kysely";
import type { HTMLInputTypeAttribute, InputHTMLAttributes } from "react";
@@ -38,32 +38,32 @@ export const baseFieldConfigSchema = Type.Object(
Type.Union(
[
Type.Boolean({ title: "Boolean", default: DEFAULT_FILLABLE }),
Type.Array(StringEnum(ActionContext), { title: "Context", uniqueItems: true })
Type.Array(StringEnum(ActionContext), { title: "Context", uniqueItems: true }),
],
{
default: DEFAULT_FILLABLE
}
)
default: DEFAULT_FILLABLE,
},
),
),
hidden: Type.Optional(
Type.Union(
[
Type.Boolean({ title: "Boolean", default: DEFAULT_HIDDEN }),
// @todo: tmp workaround
Type.Array(StringEnum(TmpContext), { title: "Context", uniqueItems: true })
Type.Array(StringEnum(TmpContext), { title: "Context", uniqueItems: true }),
],
{
default: DEFAULT_HIDDEN
}
)
default: DEFAULT_HIDDEN,
},
),
),
// if field is virtual, it will not call transformPersist & transformRetrieve
virtual: Type.Optional(Type.Boolean()),
default_value: Type.Optional(Type.Any())
default_value: Type.Optional(Type.Any()),
},
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type BaseFieldConfig = Static<typeof baseFieldConfigSchema>;
@@ -72,7 +72,7 @@ export type SchemaResponse = [string, ColumnDataType, ColumnBuilderCallback] | u
export abstract class Field<
Config extends BaseFieldConfig = BaseFieldConfig,
Type = any,
Required extends true | false = false
Required extends true | false = false,
> {
_required!: Required;
_type!: Type;
@@ -108,7 +108,7 @@ export abstract class Field<
protected useSchemaHelper(
type: ColumnDataType,
builder?: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder
builder?: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder,
): SchemaResponse {
return [
this.name,
@@ -116,7 +116,7 @@ export abstract class Field<
(col: ColumnDefinitionBuilder) => {
if (builder) return builder(col);
return col;
}
},
];
}
@@ -189,7 +189,7 @@ export abstract class Field<
getHtmlConfig(): { element: HTMLInputTypeAttribute | string; props?: InputHTMLAttributes<any> } {
return {
element: "input",
props: { type: "text" }
props: { type: "text" },
};
}
@@ -217,7 +217,7 @@ export abstract class Field<
async transformPersist(
value: unknown,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<any> {
if (this.nullish(value)) {
if (this.isRequired() && !this.hasDefault()) {
@@ -245,7 +245,7 @@ export abstract class Field<
return {
// @todo: current workaround because of fixed string type
type: this.type as any,
config: this.config
config: this.config,
};
}
}

View File

@@ -83,7 +83,7 @@ export class JsonField<Required extends true | false = false, TypeOverride = obj
override async transformPersist(
_value: any,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<string | undefined> {
const value = await super.transformPersist(_value, em, context);
//console.log("value", value);
@@ -91,7 +91,7 @@ export class JsonField<Required extends true | false = false, TypeOverride = obj
if (!this.isSerializable(value)) {
throw new TransformPersistFailedException(
`Field "${this.name}" must be serializable to JSON.`
`Field "${this.name}" must be serializable to JSON.`,
);
}

View File

@@ -9,20 +9,20 @@ export const jsonSchemaFieldConfigSchema = Type.Composite(
Type.Object({
schema: Type.Object({}, { default: {} }),
ui_schema: Type.Optional(Type.Object({})),
default_from_schema: Type.Optional(Type.Boolean())
default_from_schema: Type.Optional(Type.Boolean()),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type JsonSchemaFieldConfig = Static<typeof jsonSchemaFieldConfigSchema>;
export class JsonSchemaField<
Required extends true | false = false,
TypeOverride = object
TypeOverride = object,
> extends Field<JsonSchemaFieldConfig, TypeOverride, Required> {
override readonly type = "jsonschema";
private validator: Validator;
@@ -107,7 +107,7 @@ export class JsonSchemaField<
override async transformPersist(
_value: any,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<string | undefined> {
const value = await super.transformPersist(_value, em, context);
if (this.nullish(value)) return value;
@@ -130,8 +130,8 @@ export class JsonSchemaField<
return this.toSchemaWrapIfRequired(
FromSchema({
default: this.getDefault(),
...schema
})
...schema,
}),
);
}
}

View File

@@ -11,13 +11,13 @@ export const numberFieldConfigSchema = Type.Composite(
maximum: Type.Optional(Type.Number()),
exclusiveMinimum: Type.Optional(Type.Number()),
exclusiveMaximum: Type.Optional(Type.Number()),
multipleOf: Type.Optional(Type.Number())
multipleOf: Type.Optional(Type.Number()),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type NumberFieldConfig = Static<typeof numberFieldConfigSchema>;
@@ -39,8 +39,8 @@ export class NumberField<Required extends true | false = false> extends Field<
props: {
type: "number",
pattern: "d*",
inputMode: "numeric"
} as any // @todo: react expects "inputMode", but type dictates "inputmode"
inputMode: "numeric",
} as any, // @todo: react expects "inputMode", but type dictates "inputmode"
};
}
@@ -62,7 +62,7 @@ export class NumberField<Required extends true | false = false> extends Field<
override async transformPersist(
_value: unknown,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<number | undefined> {
const value = await super.transformPersist(_value, em, context);
@@ -72,13 +72,13 @@ export class NumberField<Required extends true | false = false> extends Field<
if (this.config.maximum && (value as number) > this.config.maximum) {
throw new TransformPersistFailedException(
`Field "${this.name}" cannot be greater than ${this.config.maximum}`
`Field "${this.name}" cannot be greater than ${this.config.maximum}`,
);
}
if (this.config.minimum && (value as number) < this.config.minimum) {
throw new TransformPersistFailedException(
`Field "${this.name}" cannot be less than ${this.config.minimum}`
`Field "${this.name}" cannot be less than ${this.config.minimum}`,
);
}
@@ -93,8 +93,8 @@ export class NumberField<Required extends true | false = false> extends Field<
maximum: this.config?.maximum,
exclusiveMinimum: this.config?.exclusiveMinimum,
exclusiveMaximum: this.config?.exclusiveMaximum,
multipleOf: this.config?.multipleOf
})
multipleOf: this.config?.multipleOf,
}),
);
}
}

View File

@@ -5,8 +5,8 @@ import { Field, baseFieldConfigSchema } from "./Field";
export const primaryFieldConfigSchema = Type.Composite([
Type.Omit(baseFieldConfigSchema, ["required"]),
Type.Object({
required: Type.Optional(Type.Literal(false))
})
required: Type.Optional(Type.Literal(false)),
}),
]);
export type PrimaryFieldConfig = Static<typeof primaryFieldConfigSchema>;

View File

@@ -19,19 +19,19 @@ export const textFieldConfigSchema = Type.Composite(
{
additionalProperties: Type.Union([
Type.String({ title: "String" }),
Type.Number({ title: "Number" })
])
}
)
)
})
)
Type.Number({ title: "Number" }),
]),
},
),
),
}),
),
}),
baseFieldConfigSchema
baseFieldConfigSchema,
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
export type TextFieldConfig = Static<typeof textFieldConfigSchema>;
@@ -81,7 +81,7 @@ export class TextField<Required extends true | false = false> extends Field<
override async transformPersist(
_value: any,
em: EntityManager<any>,
context: TActionContext
context: TActionContext,
): Promise<string | undefined> {
let value = await super.transformPersist(_value, em, context);
@@ -94,19 +94,19 @@ export class TextField<Required extends true | false = false> extends Field<
if (this.config.maxLength && value?.length > this.config.maxLength) {
throw new TransformPersistFailedException(
`Field "${this.name}" must be at most ${this.config.maxLength} character(s)`
`Field "${this.name}" must be at most ${this.config.maxLength} character(s)`,
);
}
if (this.config.minLength && value?.length < this.config.minLength) {
throw new TransformPersistFailedException(
`Field "${this.name}" must be at least ${this.config.minLength} character(s)`
`Field "${this.name}" must be at least ${this.config.minLength} character(s)`,
);
}
if (this.config.pattern && value && !new RegExp(this.config.pattern).test(value)) {
throw new TransformPersistFailedException(
`Field "${this.name}" must match the pattern ${this.config.pattern}`
`Field "${this.name}" must match the pattern ${this.config.pattern}`,
);
}
@@ -119,8 +119,8 @@ export class TextField<Required extends true | false = false> extends Field<
default: this.getDefault(),
minLength: this.config?.minLength,
maxLength: this.config?.maxLength,
pattern: this.config?.pattern
})
pattern: this.config?.pattern,
}),
);
}
}

View File

@@ -25,8 +25,8 @@ export class VirtualField extends Field<VirtualFieldConfig> {
return this.toSchemaWrapIfRequired(
Type.Any({
default: this.getDefault(),
readOnly: true
})
readOnly: true,
}),
);
}
}

View File

@@ -5,7 +5,7 @@ import { JsonField, type JsonFieldConfig, jsonFieldConfigSchema } from "./JsonFi
import {
JsonSchemaField,
type JsonSchemaFieldConfig,
jsonSchemaFieldConfigSchema
jsonSchemaFieldConfigSchema,
} from "./JsonSchemaField";
import { NumberField, type NumberFieldConfig, numberFieldConfigSchema } from "./NumberField";
import { PrimaryField, type PrimaryFieldConfig, primaryFieldConfigSchema } from "./PrimaryField";
@@ -35,7 +35,7 @@ export {
type NumberFieldConfig,
TextField,
textFieldConfigSchema,
type TextFieldConfig
type TextFieldConfig,
};
export * from "./Field";
@@ -51,5 +51,5 @@ export const FieldClassMap = {
date: { schema: dateFieldConfigSchema, field: DateField },
enum: { schema: enumFieldConfigSchema, field: EnumField },
json: { schema: jsonFieldConfigSchema, field: JsonField },
jsonschema: { schema: jsonSchemaFieldConfigSchema, field: JsonSchemaField }
jsonschema: { schema: jsonSchemaFieldConfigSchema, field: JsonSchemaField },
} as const;

View File

@@ -6,7 +6,7 @@ export class EntityIndex {
public entity: Entity,
public fields: Field[],
public unique: boolean = false,
public name?: string
public name?: string,
) {
if (fields.length === 0) {
throw new Error("Indices must contain at least one field");
@@ -21,7 +21,7 @@ export class EntityIndex {
throw new Error(
`Unique indices must have first field as required: ${fields
.map((f) => f.name)
.join(", ")}`
.join(", ")}`,
);
}
}
@@ -30,7 +30,7 @@ export class EntityIndex {
this.name = [
unique ? "idx_unique" : "idx",
entity.name,
...fields.map((f) => f.name)
...fields.map((f) => f.name),
].join("_");
}
}
@@ -40,7 +40,7 @@ export class EntityIndex {
entity: this.entity.name,
fields: this.fields.map((f) => f.name),
//name: this.name,
unique: this.unique
unique: this.unique,
};
}
}

View File

@@ -8,7 +8,7 @@ export function getDefaultValues(fields: Field[], data: EntityData): EntityData
// form fields don't like "null" or "undefined", so return empty string
acc[field.name] = field.getValue(data?.[field.name], "form") ?? "";
},
{} as EntityData
{} as EntityData,
);
}
@@ -16,7 +16,7 @@ export function getChangeSet(
action: string,
formData: EntityData,
data: EntityData,
fields: Field[]
fields: Field[],
): EntityData {
//console.log("getChangeSet", formData, data);
return transform(
@@ -45,7 +45,7 @@ export function getChangeSet(
//console.log("no change", key, value, data[key]);
}
},
{} as typeof formData
{} as typeof formData,
);
}
@@ -54,17 +54,17 @@ export function readableEmJson(_em: EntityManager) {
entities: _em.entities.map((e) => ({
name: e.name,
fields: e.fields.map((f) => f.name),
type: e.type
type: e.type,
})),
indices: _em.indices.map((i) => ({
name: i.name,
entity: i.entity.name,
fields: i.fields.map((f) => f.name),
unique: i.unique
unique: i.unique,
})),
relations: _em.relations.all.map((r) => ({
name: r.getName(),
...r.toJSON()
}))
...r.toJSON(),
})),
};
}

View File

@@ -11,7 +11,7 @@ export {
type RepoQueryIn,
defaultQuerySchema,
querySchema,
whereSchema
whereSchema,
} from "./server/data-query-impl";
export { Connection } from "./connection/Connection";
@@ -25,7 +25,7 @@ export { constructEntity, constructRelation } from "./schema/constructor";
export const DatabaseEvents = {
...MutatorEvents,
...RepositoryEvents
...RepositoryEvents,
};
export { MutatorEvents, RepositoryEvents };

View File

@@ -13,9 +13,7 @@ export class DeserializeJsonValuesPlugin implements KyselyPlugin {
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
return args.node;
}
transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
return Promise.resolve({
...args.result,
rows: args.result.rows.map((row: KeyValueObject) => {

View File

@@ -31,7 +31,7 @@ import {
type PolymorphicRelationConfig,
type TEntityType,
TextField,
type TextFieldConfig
type TextFieldConfig,
} from "../index";
type Options<Config = any> = {
@@ -56,44 +56,44 @@ const FieldMap = {
media: (o: Options) =>
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required }),
medium: (o: Options) =>
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required })
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required }),
} as const;
type TFieldType = keyof typeof FieldMap;
export function text(
config?: Omit<TextFieldConfig, "required">
config?: Omit<TextFieldConfig, "required">,
): TextField<false> & { required: () => TextField<true> } {
return new FieldPrototype("text", config, false) as any;
}
export function number(
config?: Omit<NumberFieldConfig, "required">
config?: Omit<NumberFieldConfig, "required">,
): NumberField<false> & { required: () => NumberField<true> } {
return new FieldPrototype("number", config, false) as any;
}
export function date(
config?: Omit<DateFieldConfig, "required" | "type">
config?: Omit<DateFieldConfig, "required" | "type">,
): DateField<false> & { required: () => DateField<true> } {
return new FieldPrototype("date", { ...config, type: "date" }, false) as any;
}
export function datetime(
config?: Omit<DateFieldConfig, "required" | "type">
config?: Omit<DateFieldConfig, "required" | "type">,
): DateField<false> & { required: () => DateField<true> } {
return new FieldPrototype("date", { ...config, type: "datetime" }, false) as any;
}
export function week(
config?: Omit<DateFieldConfig, "required" | "type">
config?: Omit<DateFieldConfig, "required" | "type">,
): DateField<false> & { required: () => DateField<true> } {
return new FieldPrototype("date", { ...config, type: "week" }, false) as any;
}
export function boolean(
config?: Omit<BooleanFieldConfig, "required">
config?: Omit<BooleanFieldConfig, "required">,
): BooleanField<false> & { required: () => BooleanField<true> } {
return new FieldPrototype("boolean", config, false) as any;
}
export function enumm<TypeOverride = string>(
config?: Omit<EnumFieldConfig, "required" | "options"> & {
enum: string[] | { label: string; value: string }[];
}
},
): EnumField<false, TypeOverride> & {
required: () => EnumField<true, TypeOverride>;
} {
@@ -101,18 +101,18 @@ export function enumm<TypeOverride = string>(
const actual_config = {
options: {
type,
values: config?.enum ?? []
}
values: config?.enum ?? [],
},
};
return new FieldPrototype("enumm", actual_config, false) as any;
}
export function json<TypeOverride = object>(
config?: Omit<JsonFieldConfig, "required">
config?: Omit<JsonFieldConfig, "required">,
): JsonField<false, TypeOverride> & { required: () => JsonField<true, TypeOverride> } {
return new FieldPrototype("json", config, false) as any;
}
export function jsonSchema<TypeOverride = object>(
config?: Omit<JsonSchemaFieldConfig, "required">
config?: Omit<JsonSchemaFieldConfig, "required">,
): JsonField<false, TypeOverride> & { required: () => JsonSchemaField<true, TypeOverride> } {
return new FieldPrototype("jsonSchema", config, false) as any;
}
@@ -120,7 +120,7 @@ export function media(config?: Omit<MediaFieldConfig, "entity">): MediaField<fal
return new FieldPrototype("media", config, false) as any;
}
export function medium(
config?: Omit<MediaFieldConfig, "required" | "entity" | "max_items">
config?: Omit<MediaFieldConfig, "required" | "entity" | "max_items">,
): MediaField<false, MediaItem> {
return new FieldPrototype("media", { ...config, max_items: 1 }, false) as any;
}
@@ -135,7 +135,7 @@ export class FieldPrototype {
constructor(
public type: TFieldType,
public config: any,
public is_required: boolean
public is_required: boolean,
) {}
required() {
@@ -163,7 +163,7 @@ export class FieldPrototype {
entity: { name: "unknown", fields: {} },
field_name,
config: this.config,
is_required: this.is_required
is_required: this.is_required,
}) as unknown as Field;
} catch (e) {
throw new Error(`Faild to construct field "${this.type}": ${e}`);
@@ -175,12 +175,12 @@ export class FieldPrototype {
export function entity<
EntityName extends string,
Fields extends Record<string, Field<any, any, any>>
Fields extends Record<string, Field<any, any, any>>,
>(
name: EntityName,
fields: Fields,
config?: EntityConfig,
type?: TEntityType
type?: TEntityType,
): Entity<EntityName, Fields> {
const _fields: Field[] = [];
for (const [field_name, field] of Object.entries(fields)) {
@@ -189,7 +189,7 @@ export function entity<
entity: { name, fields },
field_name,
config: f.config,
is_required: f.is_required
is_required: f.is_required,
};
_fields.push(f.getField(o));
}
@@ -207,7 +207,7 @@ export function relation<Local extends Entity>(local: Local) {
manyToMany: <Foreign extends Entity>(
foreign: Foreign,
config?: ManyToManyRelationConfig,
additionalFields?: Record<string, Field<any, any, any>>
additionalFields?: Record<string, Field<any, any, any>>,
) => {
const add_fields: Field[] = [];
if (additionalFields) {
@@ -221,7 +221,7 @@ export function relation<Local extends Entity>(local: Local) {
entity: { name: entity_name, fields },
field_name,
config: f.config,
is_required: f.is_required
is_required: f.is_required,
};
_fields.push(f.getField(o));
}
@@ -232,16 +232,16 @@ export function relation<Local extends Entity>(local: Local) {
},
polyToOne: <Foreign extends Entity>(
foreign: Foreign,
config?: Omit<PolymorphicRelationConfig, "targetCardinality">
config?: Omit<PolymorphicRelationConfig, "targetCardinality">,
) => {
return new PolymorphicRelation(local, foreign, { ...config, targetCardinality: 1 });
},
polyToMany: <Foreign extends Entity>(
foreign: Foreign,
config?: PolymorphicRelationConfig
config?: PolymorphicRelationConfig,
) => {
return new PolymorphicRelation(local, foreign, config);
}
},
};
}
@@ -256,7 +256,7 @@ export function index<E extends Entity>(entity: E) {
return field;
});
return new EntityIndex(entity, _fields, unique);
}
},
};
}
@@ -266,7 +266,7 @@ class EntityManagerPrototype<Entities extends Record<string, Entity>> extends En
constructor(
public __entities: Entities,
relations: EntityRelation[] = [],
indices: EntityIndex[] = []
indices: EntityIndex[] = [],
) {
super(Object.values(__entities), new DummyConnection(), relations, indices);
}
@@ -279,7 +279,7 @@ type Chained<R extends Record<string, (...args: any[]) => any>> = {
};
type ChainedFn<
Fn extends (...args: any[]) => Record<string, (...args: any[]) => any>,
Return extends ReturnType<Fn> = ReturnType<Fn>
Return extends ReturnType<Fn> = ReturnType<Fn>,
> = (e: Entity) => {
[K in keyof Return]: (...args: Parameters<Return[K]>) => Chained<Return>;
};
@@ -288,8 +288,8 @@ export function em<Entities extends Record<string, Entity>>(
entities: Entities,
schema?: (
fns: { relation: ChainedFn<typeof relation>; index: ChainedFn<typeof index> },
entities: Entities
) => void
entities: Entities,
) => void,
) {
const relations: EntityRelation[] = [];
const indices: EntityIndex[] = [];
@@ -301,7 +301,7 @@ export function em<Entities extends Record<string, Entity>>(
relations.push(target[prop](...args));
return relationProxy(e);
};
}
},
}) as any;
};
@@ -312,7 +312,7 @@ export function em<Entities extends Record<string, Entity>>(
indices.push(target[prop](...args));
return indexProxy(e);
};
}
},
}) as any;
};
@@ -327,7 +327,7 @@ export function em<Entities extends Record<string, Entity>>(
relations,
indices,
toJSON: () =>
e.toJSON() as unknown as Pick<ModuleConfigs["data"], "entities" | "relations" | "indices">
e.toJSON() as unknown as Pick<ModuleConfigs["data"], "entities" | "relations" | "indices">,
};
}
@@ -367,7 +367,7 @@ type OptionalUndefined<
? undefined extends T[Props]
? Props
: never
: never
: never,
> = Merge<
{
[K in OptionsProps]?: T[K];

View File

@@ -4,7 +4,7 @@ import type { Entity, EntityData, EntityManager } from "../entities";
import {
type EntityRelationAnchor,
type MutationInstructionResponse,
RelationHelper
RelationHelper,
} from "../relations";
import type { RepoQuery } from "../server/data-query-impl";
import type { RelationType } from "./relation-types";
@@ -25,7 +25,7 @@ export type BaseRelationConfig = Static<typeof EntityRelation.schema>;
// @todo: add generic type for relation config
export abstract class EntityRelation<
Schema extends typeof EntityRelation.schema = typeof EntityRelation.schema
Schema extends typeof EntityRelation.schema = typeof EntityRelation.schema,
> {
config: Static<Schema>;
@@ -39,14 +39,14 @@ export abstract class EntityRelation<
static schema = Type.Object({
mappedBy: Type.Optional(Type.String()),
inversedBy: Type.Optional(Type.String()),
required: Type.Optional(Type.Boolean())
required: Type.Optional(Type.Boolean()),
});
// don't make protected, App requires it to instantiatable
constructor(
source: EntityRelationAnchor,
target: EntityRelationAnchor,
config: Partial<Static<Schema>> = {}
config: Partial<Static<Schema>> = {},
) {
this.source = source;
this.target = target;
@@ -67,13 +67,13 @@ export abstract class EntityRelation<
*/
abstract buildWith(
entity: Entity,
reference: string
reference: string,
): (eb: ExpressionBuilder<any, any>) => KyselyQueryBuilder;
abstract buildJoin(
entity: Entity,
qb: KyselyQueryBuilder,
reference: string
reference: string,
): KyselyQueryBuilder;
getReferenceQuery(entity: Entity, id: number, reference: string): Partial<RepoQuery> {
@@ -105,7 +105,7 @@ export abstract class EntityRelation<
throw new Error(
`Entity "${entity_name}" is not part of the relation ` +
`"${this.source.entity.name} <-> ${this.target.entity.name}"`
`"${this.source.entity.name} <-> ${this.target.entity.name}"`,
);
}
@@ -141,7 +141,7 @@ export abstract class EntityRelation<
if (Array.isArray(hydrated) && hydrated.length > 1) {
throw new Error(
`Failed to hydrate "${anchor.entity.name}" ` +
`with value: ${JSON.stringify(value)} (cardinality: 1)`
`with value: ${JSON.stringify(value)} (cardinality: 1)`,
);
}
@@ -151,7 +151,7 @@ export abstract class EntityRelation<
if (!hydrated) {
throw new Error(
`Failed to hydrate "${anchor.entity.name}" ` +
`with value: ${JSON.stringify(value)} (cardinality: -)`
`with value: ${JSON.stringify(value)} (cardinality: -)`,
);
}
@@ -178,7 +178,7 @@ export abstract class EntityRelation<
async $set(
em: EntityManager<any>,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
throw new Error("$set is not allowed");
}
@@ -186,7 +186,7 @@ export abstract class EntityRelation<
async $create(
em: EntityManager<any>,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
throw new Error("$create is not allowed");
}
@@ -194,7 +194,7 @@ export abstract class EntityRelation<
async $attach(
em: EntityManager<any>,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
throw new Error("$attach is not allowed");
}
@@ -202,7 +202,7 @@ export abstract class EntityRelation<
async $detach(
em: EntityManager<any>,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
throw new Error("$detach is not allowed");
}
@@ -213,7 +213,7 @@ export abstract class EntityRelation<
this.source.entity.name,
this.target.entity.name,
this.config.mappedBy,
this.config.inversedBy
this.config.inversedBy,
].filter(Boolean);
return parts.join("_");
}
@@ -223,7 +223,7 @@ export abstract class EntityRelation<
type: this.type(),
source: this.source.entity.name,
target: this.target.entity.name,
config: this.config
config: this.config,
};
}
}

View File

@@ -21,19 +21,19 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
EntityRelation.schema,
Type.Object({
connectionTable: Type.Optional(Type.String()),
connectionTableMappedName: Type.Optional(Type.String())
})
connectionTableMappedName: Type.Optional(Type.String()),
}),
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
constructor(
source: Entity,
target: Entity,
config?: ManyToManyRelationConfig,
additionalFields?: Field[]
additionalFields?: Field[],
) {
const connectionTable =
config?.connectionTable || ManyToManyRelation.defaultConnectionTable(source, target);
@@ -67,12 +67,12 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
getField(entity: Entity): RelationField {
const conn = this.connectionEntity;
const selfField = conn.fields.find(
(f) => f instanceof RelationField && f.target() === entity.name
(f) => f instanceof RelationField && f.target() === entity.name,
)!;
if (!selfField || !(selfField instanceof RelationField)) {
throw new Error(
`Connection entity "${conn.name}" does not have a relation to "${entity.name}"`
`Connection entity "${conn.name}" does not have a relation to "${entity.name}"`,
);
}
@@ -87,7 +87,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
const join = [
conn.name,
`${other.entity.name}.${other.entity.getPrimaryField().name}`,
`${conn.name}.${otherField.name}`
`${conn.name}.${otherField.name}`,
] as const;
const entityRef = `${entity.name}.${entity.getPrimaryField().name}`;
@@ -100,7 +100,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
join,
entityRef,
otherRef,
groupBy
groupBy,
};
}
@@ -109,9 +109,9 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
return {
where: {
[otherRef]: id
[otherRef]: id,
},
join: [other.reference]
join: [other.reference],
};
}
@@ -136,7 +136,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
const limit = 5;
const { other, join, entityRef, otherRef } = this.getQueryInfo(entity);
const additionalFields = this.connectionEntity.fields.filter(
(f) => !(f instanceof RelationField || f instanceof PrimaryField)
(f) => !(f instanceof RelationField || f instanceof PrimaryField),
);
return (eb: ExpressionBuilder<any, any>) =>
@@ -149,9 +149,9 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
select.push(
jsonBuildObject(
Object.fromEntries(
additionalFields.map((f) => [f.name, eb2.ref(`${conn}.${f.name}`)])
)
).as(this.connectionTableMappedName)
additionalFields.map((f) => [f.name, eb2.ref(`${conn}.${f.name}`)]),
),
).as(this.connectionTableMappedName),
);
}
@@ -186,7 +186,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
override getName(): string {
return [
super.getName(),
[this.connectionEntity.name, this.connectionTableMappedName].filter(Boolean)
[this.connectionEntity.name, this.connectionTableMappedName].filter(Boolean),
].join("_");
}
}

View File

@@ -23,7 +23,7 @@ export type ManyToOneRelationConfig = Static<typeof ManyToOneRelation.schema>;
export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.schema> {
private fieldConfig?: RelationFieldBaseConfig;
static DEFAULTS = {
with_limit: 5
with_limit: 5,
};
static override schema = Type.Composite(
@@ -32,24 +32,24 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
Type.Object({
sourceCardinality: Type.Optional(Type.Number()),
with_limit: Type.Optional(
Type.Number({ default: ManyToOneRelation.DEFAULTS.with_limit })
Type.Number({ default: ManyToOneRelation.DEFAULTS.with_limit }),
),
fieldConfig: Type.Optional(
Type.Object({
label: Type.String()
})
)
})
label: Type.String(),
}),
),
}),
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
constructor(
source: Entity,
target: Entity,
config: Partial<Static<typeof ManyToOneRelation.schema>> = {}
config: Partial<Static<typeof ManyToOneRelation.schema>> = {},
) {
const mappedBy = config.mappedBy || target.name;
const inversedBy = config.inversedBy || source.name;
@@ -78,15 +78,15 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
// add required mapping field on source
const field = RelationField.create(this, this.target, {
label: defaultLabel,
...this.fieldConfig
...this.fieldConfig,
});
if (!this.source.entity.field(field.name)) {
this.source.entity.addField(
RelationField.create(this, this.target, {
label: defaultLabel,
...this.fieldConfig
})
...this.fieldConfig,
}),
);
}
}
@@ -100,7 +100,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
if (!(field instanceof RelationField)) {
throw new Error(
`Field "${this.target.reference}_${id}" not found on entity "${this.source.entity.name}"`
`Field "${this.target.reference}_${id}" not found on entity "${this.source.entity.name}"`,
);
}
@@ -133,7 +133,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
relationRef,
entityRef,
otherRef,
groupBy
groupBy,
};
}
@@ -145,9 +145,9 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
return {
where: {
[otherRef]: id
[otherRef]: id,
},
join: other.entity.name === self.entity.name ? [] : [other.entity.name]
join: other.entity.name === self.entity.name ? [] : [other.entity.name],
};
}
@@ -176,7 +176,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
override async $set(
em: EntityManager<any>,
key: string,
value: object
value: object,
): Promise<void | MutationInstructionResponse> {
if (typeof value !== "object") {
throw new Error(`Invalid value for relation field "${key}" given, expected object.`);
@@ -204,14 +204,14 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
}
const query = await em.repository(field.target()).exists({
[field.targetField()]: primaryReference as any
[field.targetField()]: primaryReference as any,
});
if (!query.exists) {
const idProp = field.targetField();
throw new Error(
`Cannot connect "${entity.name}.${key}" to ` +
`"${field.target()}.${idProp}" = "${primaryReference}": not found.`
`"${field.target()}.${idProp}" = "${primaryReference}": not found.`,
);
}

View File

@@ -22,7 +22,7 @@ export class OneToOneRelation extends ManyToOneRelation {
mappedBy,
inversedBy,
sourceCardinality: 1,
required
required,
});
}
@@ -41,7 +41,7 @@ export class OneToOneRelation extends ManyToOneRelation {
override async $set(
em: EntityManager<any>,
key: string,
value: object
value: object,
): Promise<MutationInstructionResponse> {
throw new Error("$set is not allowed");
}
@@ -49,7 +49,7 @@ export class OneToOneRelation extends ManyToOneRelation {
override async $create(
em: EntityManager<any>,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
if (value === null || typeof value !== "object") {
throw new Error(`Invalid value for relation field "${key}" given, expected object.`);

View File

@@ -15,12 +15,12 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
[
EntityRelation.schema,
Type.Object({
targetCardinality: Type.Optional(Type.Number())
})
targetCardinality: Type.Optional(Type.Number()),
}),
],
{
additionalProperties: false
}
additionalProperties: false,
},
);
constructor(source: Entity, target: Entity, config: Partial<PolymorphicRelationConfig> = {}) {
@@ -63,7 +63,7 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
reference_other,
entityRef,
otherRef,
groupBy
groupBy,
};
}
@@ -72,7 +72,7 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
return qb
.innerJoin(other.entity.name, (join) =>
join.onRef(entityRef, "=", otherRef).on(whereLhs, "=", reference)
join.onRef(entityRef, "=", otherRef).on(whereLhs, "=", reference),
)
.groupBy(groupBy);
}
@@ -83,8 +83,8 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
return {
where: {
[this.getReferenceField().name]: info.reference_other,
[this.getEntityIdField().name]: id
}
[this.getEntityIdField().name]: id,
},
};
}

View File

@@ -12,8 +12,8 @@ export const relationFieldConfigSchema = Type.Composite([
reference: Type.String(),
target: Type.String(), // @todo: potentially has to be an instance!
target_field: Type.Optional(Type.String({ default: "id" })),
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" }))
})
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" })),
}),
]);
/*export const relationFieldConfigSchema = baseFieldConfigSchema.extend({
reference: z.string(),
@@ -44,11 +44,11 @@ export class RelationField extends Field<RelationFieldConfig> {
static create(
relation: EntityRelation,
target: EntityRelationAnchor,
config?: RelationFieldBaseConfig
config?: RelationFieldBaseConfig,
) {
const name = [
target.reference ?? target.entity.name,
target.entity.getPrimaryField().name
target.entity.getPrimaryField().name,
].join("_");
//console.log('name', name);
return new RelationField(name, {
@@ -56,7 +56,7 @@ export class RelationField extends Field<RelationFieldConfig> {
required: relation.required,
reference: target.reference,
target: target.entity.name,
target_field: target.entity.getPrimaryField().name
target_field: target.entity.getPrimaryField().name,
});
}
@@ -94,8 +94,8 @@ export class RelationField extends Field<RelationFieldConfig> {
override toJsonSchema() {
return this.toSchemaWrapIfRequired(
Type.Number({
$ref: `${this.config?.target}#/properties/${this.config?.target_field}`
})
$ref: `${this.config?.target}#/properties/${this.config?.target_field}`,
}),
);
}
}

View File

@@ -5,7 +5,7 @@ import {
ManyToManyRelation,
type MutationOperation,
MutationOperations,
RelationField
RelationField,
} from "../relations";
export type MutationInstructionResponse = [string, PrimaryFieldType | null];
@@ -13,7 +13,7 @@ export type MutationInstructionResponse = [string, PrimaryFieldType | null];
export class RelationMutator {
constructor(
protected entity: Entity,
protected em: EntityManager<any>
protected em: EntityManager<any>,
) {}
/**
@@ -32,11 +32,11 @@ export class RelationMutator {
// @todo: improve later
if (this.entity.type === "generated") {
const relation = this.em.relations.all.find(
(r) => r instanceof ManyToManyRelation && r.connectionEntity.name === this.entity.name
(r) => r instanceof ManyToManyRelation && r.connectionEntity.name === this.entity.name,
);
if (relation instanceof ManyToManyRelation) {
references.push(
...this.entity.fields.filter((f) => f.type === "relation").map((f) => f.name)
...this.entity.fields.filter((f) => f.type === "relation").map((f) => f.name),
);
}
}
@@ -53,7 +53,7 @@ export class RelationMutator {
async persistRelationField(
field: RelationField,
key: string,
value: PrimaryFieldType
value: PrimaryFieldType,
): Promise<MutationInstructionResponse> {
// allow empty if field is not required
if (value === null && !field.isRequired()) {
@@ -68,14 +68,14 @@ export class RelationMutator {
}
const query = await this.em.repository(field.target()).exists({
[field.targetField()]: value
[field.targetField()]: value,
});
if (!query.exists) {
const idProp = field.targetField();
throw new Error(
`Cannot connect "${this.entity.name}.${key}" to ` +
`"${field.target()}.${idProp}" = "${value}": not found.`
`"${field.target()}.${idProp}" = "${value}": not found.`,
);
}
@@ -85,11 +85,11 @@ export class RelationMutator {
async persistReference(
relation: EntityRelation,
key: string,
value: unknown
value: unknown,
): Promise<void | MutationInstructionResponse> {
if (typeof value !== "object" || value === null || typeof value === "undefined") {
throw new Error(
`Invalid value for relation "${key}" given, expected object to persist reference. Like '{$set: {id: 1}}'.`
`Invalid value for relation "${key}" given, expected object to persist reference. Like '{$set: {id: 1}}'.`,
);
}
@@ -97,7 +97,7 @@ export class RelationMutator {
if (!MutationOperations.includes(operation)) {
throw new Error(
`Invalid operation "${operation}" for relation "${key}". ` +
`Allowed: ${MutationOperations.join(", ")}`
`Allowed: ${MutationOperations.join(", ")}`,
);
}
@@ -131,7 +131,7 @@ export class RelationMutator {
throw new Error(
`Relation "${key}" failed to resolve on entity "${this.entity.name}": ` +
"Unable to resolve relation origin."
"Unable to resolve relation origin.",
);
}
}

View File

@@ -14,7 +14,7 @@ import {
RelationField,
type RelationFieldBaseConfig,
type RelationFieldConfig,
relationFieldConfigSchema
relationFieldConfigSchema,
} from "./RelationField";
export {
@@ -32,7 +32,7 @@ export {
RelationField,
relationFieldConfigSchema,
type RelationFieldBaseConfig,
type RelationFieldConfig
type RelationFieldConfig,
};
export const RelationClassMap = {
@@ -41,10 +41,10 @@ export const RelationClassMap = {
[RelationTypes.ManyToMany]: { schema: ManyToManyRelation.schema, cls: ManyToManyRelation },
[RelationTypes.Polymorphic]: {
schema: PolymorphicRelation.schema,
cls: PolymorphicRelation
}
cls: PolymorphicRelation,
},
} as const;
export const RelationFieldClassMap = {
relation: { schema: relationFieldConfigSchema, field: RelationField }
relation: { schema: relationFieldConfigSchema, field: RelationField },
} as const;

View File

@@ -58,7 +58,7 @@ export class SchemaManager {
async introspect(): Promise<IntrospectedTable[]> {
const tables = await this.getIntrospector().getTables({
withInternalKyselyTables: false
withInternalKyselyTables: false,
});
const indices = await this.getIntrospector().getIndices();
@@ -71,7 +71,7 @@ export class SchemaManager {
cleanTables.push({
...table,
indices: indices.filter((index) => index.table === table.name)
indices: indices.filter((index) => index.table === table.name),
});
}
@@ -94,7 +94,7 @@ export class SchemaManager {
isNullable: true, // managed by the field
isAutoIncrementing: field instanceof PrimaryField,
hasDefaultValue: false, // managed by the field
comment: undefined
comment: undefined,
})),
indices: indices.map((index) => ({
name: index.name,
@@ -102,9 +102,9 @@ export class SchemaManager {
isUnique: index.unique,
columns: index.fields.map((f) => ({
name: f.name,
order: 0 // doesn't matter
}))
})) as any
order: 0, // doesn't matter
})),
})) as any,
};
}
@@ -131,12 +131,12 @@ export class SchemaManager {
columns: {
add: [],
drop: [],
change: []
change: [],
},
indices: {
add: [],
drop: []
}
drop: [],
},
});
});
@@ -151,22 +151,22 @@ export class SchemaManager {
columns: {
add: entity.columns.map(namesFn),
drop: [],
change: []
change: [],
},
indices: {
add: entity.indices.map(namesFn),
drop: []
}
drop: [],
},
});
} else {
// If the table exists, check for new columns
const newColumns = entity.columns.filter(
(newColumn) => !table.columns.map(namesFn).includes(newColumn.name)
(newColumn) => !table.columns.map(namesFn).includes(newColumn.name),
);
// check for columns to drop
const dropColumns = table.columns.filter(
(oldColumn) => !entity.columns.map(namesFn).includes(oldColumn.name)
(oldColumn) => !entity.columns.map(namesFn).includes(oldColumn.name),
);
// check for changed columns
@@ -179,25 +179,25 @@ export class SchemaManager {
col_diffs.push({
attribute: key,
prev: db_col[key],
next: value
next: value,
});
}
}
if (Object.keys(col_diffs).length > 0) {
columnDiffs.push({
name: entity_col.name,
changes: col_diffs
changes: col_diffs,
});
}
}
// new indices
const newIndices = entity.indices.filter(
(newIndex) => !table.indices.map((i) => i.name).includes(newIndex.name)
(newIndex) => !table.indices.map((i) => i.name).includes(newIndex.name),
);
const dropIndices = table.indices.filter(
(oldIndex) => !entity.indices.map((i) => i.name).includes(oldIndex.name)
(oldIndex) => !entity.indices.map((i) => i.name).includes(oldIndex.name),
);
const anythingChanged = [
@@ -205,7 +205,7 @@ export class SchemaManager {
dropColumns,
//columnDiffs, // ignored
newIndices,
dropIndices
dropIndices,
].some((arr) => arr.length > 0);
if (anythingChanged) {
@@ -217,12 +217,12 @@ export class SchemaManager {
drop: dropColumns.map(namesFn),
// @todo: this is ignored for now
//change: columnDiffs.map(namesFn),
change: []
change: [],
},
indices: {
add: newIndices.map(namesFn),
drop: dropIndices.map(namesFn)
}
drop: dropIndices.map(namesFn),
},
});
}
}

View File

@@ -18,17 +18,17 @@ export function constructEntity(name: string, entityConfig: TAppDataEntity) {
name,
Object.values(fields),
entityConfig.config as any,
entityConfig.type as any
entityConfig.type as any,
);
}
export function constructRelation(
relationConfig: TAppDataRelation,
resolver: (name: Entity | string) => Entity
resolver: (name: Entity | string) => Entity,
) {
return new RELATIONS[relationConfig.type].cls(
resolver(relationConfig.source),
resolver(relationConfig.target),
relationConfig.config
relationConfig.config,
);
}

View File

@@ -6,7 +6,7 @@ import {
StringEnum,
Type,
Value,
isObject
isObject,
} from "core/utils";
import { WhereBuilder, type WhereQuery } from "../entities";
@@ -23,9 +23,9 @@ const sort = Type.Transform(
Type.Union(
[Type.String(), Type.Object({ by: Type.String(), dir: StringEnum(["asc", "desc"]) })],
{
default: sort_default
}
)
default: sort_default,
},
),
)
.Decode((value): { by: string; dir: "asc" | "desc" } => {
if (typeof value === "string") {
@@ -43,7 +43,7 @@ const sort = Type.Transform(
.Encode((value) => value);
const stringArray = Type.Transform(
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] })
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] }),
)
.Decode((value) => {
if (Array.isArray(value)) {
@@ -56,7 +56,7 @@ const stringArray = Type.Transform(
.Encode((value) => (Array.isArray(value) ? value : [value]));
export const whereSchema = Type.Transform(
Type.Union([Type.String(), Type.Object({})], { default: {} })
Type.Union([Type.String(), Type.Object({})], { default: {} }),
)
.Decode((value) => {
const q = typeof value === "string" ? JSON.parse(value) : value;
@@ -73,7 +73,7 @@ export type RepoWithSchema = Record<
export const withSchema = <TSelf extends TThis>(Self: TSelf) =>
Type.Transform(
Type.Union([Type.String(), Type.Array(Type.String()), Type.Record(Type.String(), Self)])
Type.Union([Type.String(), Type.Array(Type.String()), Type.Record(Type.String(), Self)]),
)
.Decode((value) => {
// images
@@ -130,15 +130,15 @@ export const querySchema = Type.Recursive(
select: stringArray,
with: withSchema(Self),
join: stringArray,
where: whereSchema
where: whereSchema,
},
{
// @todo: determine if unknown is allowed, it's ignore anyway
additionalProperties: false
}
)
additionalProperties: false,
},
),
),
{ $id: "query-schema" }
{ $id: "query-schema" },
);
export type RepoQueryIn = {