feat: adding env aware endpoint to obtain sqlite connection, remove libsql hard dependency

This commit is contained in:
dswbx
2025-06-13 11:09:47 +02:00
parent 046c1d21b1
commit bbb7bfb7a1
28 changed files with 288 additions and 159 deletions

View File

@@ -244,7 +244,11 @@ async function buildAdapters() {
// specific adatpers
await tsup.build(baseConfig("react-router"));
await tsup.build(baseConfig("bun"));
await tsup.build(
baseConfig("bun", {
external: [/^bun\:.*/],
}),
);
await tsup.build(baseConfig("astro"));
await tsup.build(baseConfig("aws"));
await tsup.build(
@@ -267,6 +271,29 @@ async function buildAdapters() {
...baseConfig("node"),
platform: "node",
});
await tsup.build({
...baseConfig("sqlite/edge"),
entry: ["src/adapter/sqlite/edge.ts"],
outDir: "dist/adapter/sqlite",
metafile: false,
});
await tsup.build({
...baseConfig("sqlite/node"),
entry: ["src/adapter/sqlite/node.ts"],
outDir: "dist/adapter/sqlite",
platform: "node",
metafile: false,
});
await tsup.build({
...baseConfig("sqlite/bun"),
entry: ["src/adapter/sqlite/bun.ts"],
outDir: "dist/adapter/sqlite",
metafile: false,
external: [/^bun\:.*/],
});
}
await buildApi();

View File

@@ -50,7 +50,6 @@
"@codemirror/lang-json": "^6.0.1",
"@hello-pangea/dnd": "^18.0.1",
"@hono/swagger-ui": "^0.5.1",
"@libsql/client": "^0.15.2",
"@mantine/core": "^7.17.1",
"@mantine/hooks": "^7.17.1",
"@sinclair/typebox": "0.34.30",
@@ -61,11 +60,11 @@
"bcryptjs": "^3.0.2",
"dayjs": "^1.11.13",
"fast-xml-parser": "^5.0.8",
"hono": "^4.7.11",
"json-schema-form-react": "^0.0.2",
"json-schema-library": "10.0.0-rc7",
"json-schema-to-ts": "^3.1.1",
"kysely": "^0.27.6",
"hono": "^4.7.11",
"lodash-es": "^4.17.21",
"oauth4webapi": "^2.11.1",
"object-path-immutable": "^4.1.2",
@@ -74,6 +73,7 @@
"uuid": "^11.1.0"
},
"devDependencies": {
"@libsql/client": "^0.15.9",
"@aws-sdk/client-s3": "^3.758.0",
"@bluwy/giget-core": "^0.1.2",
"@cloudflare/workers-types": "^4.20250606.0",
@@ -104,6 +104,7 @@
"jsonv-ts": "^0.1.0",
"kysely-d1": "^0.3.0",
"kysely-generic-sqlite": "^1.2.1",
"libsql-stateless-easy": "^1.8.0",
"open": "^10.1.0",
"openapi-types": "^12.1.3",
"picocolors": "^1.1.1",
@@ -184,6 +185,20 @@
"import": "./dist/media/index.js",
"require": "./dist/media/index.js"
},
"./adapter/sqlite": {
"types": "./dist/types/adapter/sqlite/edge.d.ts",
"import": {
"workerd": "./dist/adapter/sqlite/edge.js",
"edge-light": "./dist/adapter/sqlite/edge.js",
"netlify": "./dist/adapter/sqlite/edge.js",
"vercel": "./dist/adapter/sqlite/edge.js",
"browser": "./dist/adapter/sqlite/edge.js",
"bun": "./dist/adapter/sqlite/bun.js",
"node": "./dist/adapter/sqlite/node.js",
"default": "./dist/adapter/sqlite/node.js"
},
"require": "./dist/adapter/sqlite/node.js"
},
"./adapter/cloudflare": {
"types": "./dist/types/adapter/cloudflare/index.d.ts",
"import": "./dist/adapter/cloudflare/index.js",

View File

@@ -1,7 +1,7 @@
import type { CreateUserPayload } from "auth/AppAuth";
import { $console } from "core";
import { Event } from "core/events";
import { Connection, type LibSqlCredentials, LibsqlConnection } from "data";
import { Connection } from "data/connection/Connection";
import type { Hono } from "hono";
import {
ModuleManager,
@@ -14,10 +14,10 @@ import {
import * as SystemPermissions from "modules/permissions";
import { AdminController, type AdminControllerOptions } from "modules/server/AdminController";
import { SystemController } from "modules/server/SystemController";
import type { ServerEnv } from "modules/Controller";
// biome-ignore format: must be here
import { Api, type ApiOptions } from "Api";
import type { ServerEnv } from "modules/Controller";
export type AppPlugin = (app: App) => Promise<void> | void;
@@ -51,15 +51,8 @@ export type AppOptions = {
manager?: Omit<ModuleManagerOptions, "initial" | "onUpdated" | "seed">;
asyncEventsMode?: "sync" | "async" | "none";
};
export type CreateAppConfig = {
connection?:
| Connection
| {
// @deprecated
type: "libsql";
config: LibSqlCredentials;
}
| LibSqlCredentials;
export type CreateAppConfig<C extends Connection = Connection> = {
connection?: C | { url: string };
initialConfig?: InitialModuleConfigs;
options?: AppOptions;
};
@@ -67,7 +60,7 @@ export type CreateAppConfig = {
export type AppConfig = InitialModuleConfigs;
export type LocalApiOptions = Request | ApiOptions;
export class App {
export class App<C extends Connection = Connection> {
static readonly Events = AppEvents;
modules: ModuleManager;
@@ -79,7 +72,7 @@ export class App {
private _building: boolean = false;
constructor(
private connection: Connection,
public connection: C,
_initialConfig?: InitialModuleConfigs,
private options?: AppOptions,
) {
@@ -262,31 +255,9 @@ export class App {
}
export function createApp(config: CreateAppConfig = {}) {
let connection: Connection | undefined = undefined;
try {
if (Connection.isConnection(config.connection)) {
connection = config.connection;
} else if (typeof config.connection === "object") {
if ("type" in config.connection) {
$console.warn(
"Using deprecated connection type 'libsql', use the 'config' object directly.",
);
connection = new LibsqlConnection(config.connection.config);
} else {
connection = new LibsqlConnection(config.connection);
}
} else {
connection = new LibsqlConnection({ url: ":memory:" });
$console.warn("No connection provided, using in-memory database");
}
} catch (e) {
$console.error("Could not create connection", e);
}
if (!connection) {
if (!config.connection || !Connection.isConnection(config.connection)) {
throw new Error("Invalid connection");
}
return new App(connection, config.initialConfig, config.options);
return new App(config.connection, config.initialConfig, config.options);
}

View File

@@ -1,11 +1,18 @@
/// <reference types="bun-types" />
import path from "node:path";
import { type RuntimeBkndConfig, createRuntimeApp, type RuntimeOptions } from "bknd/adapter";
import { registerLocalMediaAdapter } from "bknd/adapter/node";
import {
type RuntimeBkndConfig,
createRuntimeApp,
type RuntimeOptions,
Connection,
} from "bknd/adapter";
import { registerLocalMediaAdapter } from ".";
import { config } from "bknd/core";
import type { ServeOptions } from "bun";
import { serveStatic } from "hono/bun";
import { sqlite } from "bknd/adapter/sqlite";
import type { App } from "App";
type BunEnv = Bun.Env;
export type BunBkndConfig<Env = BunEnv> = RuntimeBkndConfig<Env> & Omit<ServeOptions, "fetch">;
@@ -18,9 +25,17 @@ export async function createApp<Env = BunEnv>(
const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static");
registerLocalMediaAdapter();
let connection: Connection | undefined;
if (Connection.isConnection(config.connection)) {
connection = config.connection;
} else {
connection = sqlite(config.connection ?? { url: ":memory:" });
}
return await createRuntimeApp(
{
...config,
connection,
serveStatic: serveStatic({ root }),
},
args ?? (process.env as Env),
@@ -33,8 +48,11 @@ export function createHandler<Env = BunEnv>(
args: Env = {} as Env,
opts?: RuntimeOptions,
) {
let app: App | undefined;
return async (req: Request) => {
const app = await createApp(config, args ?? (process.env as Env), opts);
if (!app) {
app = await createApp(config, args ?? (process.env as Env), opts);
}
return app.fetch(req);
};
}
@@ -72,5 +90,5 @@ export function serve<Env = BunEnv>(
),
});
console.log(`Server is running on http://localhost:${port}`);
console.info(`Server is running on http://localhost:${port}`);
}

View File

@@ -1,4 +1,4 @@
import type { Database } from "bun:sqlite";
import { Database } from "bun:sqlite";
import {
buildQueryFn,
GenericSqliteConnection,
@@ -30,12 +30,19 @@ function bunSqliteExecutor(db: Database, cache: boolean): IGenericSqlite<Databas
};
}
export function bunSqlite(config: BunSqliteConnectionConfig) {
return new GenericSqliteConnection(
config.database,
() => bunSqliteExecutor(config.database, false),
{
name: "bun-sqlite",
},
);
export function bunSqlite(config?: BunSqliteConnectionConfig | { url: string }) {
let database: Database;
if (config) {
if ("database" in config) {
database = config.database;
} else {
database = new Database(config.url);
}
} else {
database = new Database(":memory:");
}
return new GenericSqliteConnection(database, () => bunSqliteExecutor(database, false), {
name: "bun-sqlite",
});
}

View File

@@ -1 +1,3 @@
export * from "./bun.adapter";
export * from "../node/storage";
export * from "./connection/BunSqliteConnection";

View File

@@ -64,7 +64,7 @@ export class DurableBkndApp extends DurableObject {
"type" in config.connection &&
config.connection.type === "libsql"
) {
config.connection.config.protocol = "wss";
//config.connection.config.protocol = "wss";
}
this.app = await createRuntimeApp({

View File

@@ -3,6 +3,8 @@ import { config as $config } from "bknd/core";
import type { MiddlewareHandler } from "hono";
import type { AdminControllerOptions } from "modules/server/AdminController";
export { Connection } from "bknd/data";
export type BkndConfig<Args = any> = CreateAppConfig & {
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
onBuilt?: (app: App) => Promise<void>;

View File

@@ -4,7 +4,7 @@ import {
parseBigInt,
type IGenericSqlite,
} from "../../../data/connection/sqlite/GenericSqliteConnection";
import type { DatabaseSync } from "node:sqlite";
import { DatabaseSync } from "node:sqlite";
export type NodeSqliteConnectionConfig = {
database: DatabaseSync;
@@ -39,8 +39,19 @@ function nodeSqliteExecutor(db: DatabaseSync): IGenericSqlite<DatabaseSync> {
};
}
export function nodeSqlite(config: NodeSqliteConnectionConfig) {
return new GenericSqliteConnection(config.database, () => nodeSqliteExecutor(config.database), {
export function nodeSqlite(config?: NodeSqliteConnectionConfig | { url: string }) {
let database: DatabaseSync;
if (config) {
if ("database" in config) {
database = config.database;
} else {
database = new DatabaseSync(config.url);
}
} else {
database = new DatabaseSync(":memory:");
}
return new GenericSqliteConnection(database, () => nodeSqliteExecutor(database), {
name: "node-sqlite",
});
}

View File

@@ -1,18 +1,3 @@
import { registries } from "bknd";
import { type LocalAdapterConfig, StorageLocalAdapter } from "./storage/StorageLocalAdapter";
export * from "./node.adapter";
export { StorageLocalAdapter, type LocalAdapterConfig };
let registered = false;
export function registerLocalMediaAdapter() {
if (!registered) {
registries.media.register("local", StorageLocalAdapter);
registered = true;
}
return (config: Partial<LocalAdapterConfig> = {}) => {
const adapter = new StorageLocalAdapter(config);
return adapter.toJSON(true);
};
}
export * from "./storage";
export * from "./connection/NodeSqliteConnection";

View File

@@ -2,9 +2,16 @@ import path from "node:path";
import { serve as honoServe } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { registerLocalMediaAdapter } from "adapter/node/index";
import { type RuntimeBkndConfig, createRuntimeApp, type RuntimeOptions } from "bknd/adapter";
import {
type RuntimeBkndConfig,
createRuntimeApp,
type RuntimeOptions,
Connection,
} from "bknd/adapter";
import { config as $config } from "bknd/core";
import { $console } from "core";
import { sqlite } from "bknd/adapter/sqlite";
import type { App } from "App";
type NodeEnv = NodeJS.ProcessEnv;
export type NodeBkndConfig<Env = NodeEnv> = RuntimeBkndConfig<Env> & {
@@ -28,10 +35,18 @@ export async function createApp<Env = NodeEnv>(
console.warn("relativeDistPath is deprecated, please use distPath instead");
}
let connection: Connection | undefined;
if (Connection.isConnection(config.connection)) {
connection = config.connection;
} else {
connection = sqlite(config.connection ?? { url: ":memory:" });
}
registerLocalMediaAdapter();
return await createRuntimeApp(
{
...config,
connection,
serveStatic: serveStatic({ root }),
},
// @ts-ignore
@@ -45,8 +60,11 @@ export function createHandler<Env = NodeEnv>(
args: Env = {} as Env,
opts?: RuntimeOptions,
) {
let app: App | undefined;
return async (req: Request) => {
const app = await createApp(config, args ?? (process.env as Env), opts);
if (!app) {
app = await createApp(config, args ?? (process.env as Env), opts);
}
return app.fetch(req);
};
}

View File

@@ -0,0 +1,17 @@
import { registries } from "bknd";
import { type LocalAdapterConfig, StorageLocalAdapter } from "./StorageLocalAdapter";
export * from "./StorageLocalAdapter";
let registered = false;
export function registerLocalMediaAdapter() {
if (!registered) {
registries.media.register("local", StorageLocalAdapter);
registered = true;
}
return (config: Partial<LocalAdapterConfig> = {}) => {
const adapter = new StorageLocalAdapter(config);
return adapter.toJSON(true);
};
}

View File

@@ -0,0 +1,8 @@
import type { Connection } from "bknd/data";
import { $console } from "bknd/core";
import { bunSqlite } from "../bun/connection/BunSqliteConnection";
export function sqlite(config: { url: string }): Connection {
$console.info("Using bun-sqlite", config);
return bunSqlite(config);
}

View File

@@ -0,0 +1,8 @@
import { $console } from "bknd/core";
import type { Connection } from "bknd/data";
import { libsql } from "../../data/connection/sqlite/LibsqlConnection";
export function sqlite(config: { url: string }): Connection {
$console.info("Using libsql", config);
return libsql(config);
}

View File

@@ -0,0 +1,8 @@
import { $console } from "bknd/core";
import type { Connection } from "bknd/data";
import { nodeSqlite } from "../node/connection/NodeSqliteConnection";
export function sqlite(config: { url: string }): Connection {
$console.info("Using node-sqlite", config);
return nodeSqlite(config);
}

View File

@@ -0,0 +1,3 @@
import type { Connection } from "bknd/data";
export type SqliteConnection = (config: { url: string }) => Connection;

View File

@@ -48,6 +48,14 @@ export function isNode() {
}
}
export function isBun() {
try {
return typeof Bun !== "undefined";
} catch (e) {
return false;
}
}
export function invariant(condition: boolean | any, message: string) {
if (!condition) {
throw new Error(message);

View File

@@ -1,5 +1,5 @@
import type { DB } from "core";
import type { EntityData, RepoQueryIn, RepositoryResponse } from "data";
import type { EntityData, RepoQueryIn, RepositoryResultJSON } from "data";
import type { Insertable, Selectable, Updateable } from "kysely";
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules";
import type { FetchPromise, ResponseObject } from "modules/ModuleApi";
@@ -32,10 +32,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {},
) {
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.get<Pick<RepositoryResponse<Data>, "meta" | "data">>(
["entity", entity as any, id],
query,
);
return this.get<RepositoryResultJSON<Data>>(["entity", entity as any, id], query);
}
readOneBy<E extends keyof DB | string>(
@@ -43,7 +40,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
query: Omit<RepoQueryIn, "limit" | "offset" | "sort"> = {},
) {
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
type T = Pick<RepositoryResponse<Data>, "meta" | "data">;
type T = RepositoryResultJSON<Data>;
return this.readMany(entity, {
...query,
limit: 1,
@@ -53,7 +50,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
readMany<E extends keyof DB | string>(entity: E, query: RepoQueryIn = {}) {
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
type T = Pick<RepositoryResponse<Data[]>, "meta" | "data">;
type T = RepositoryResultJSON<Data[]>;
const input = query ?? this.options.defaultQuery;
const req = this.get<T>(["entity", entity as any], input);
@@ -72,7 +69,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
query: RepoQueryIn = {},
) {
type Data = R extends keyof DB ? Selectable<DB[R]> : EntityData;
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
return this.get<RepositoryResultJSON<Data[]>>(
["entity", entity as any, id, reference],
query ?? this.options.defaultQuery,
);
@@ -83,7 +80,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
input: Insertable<Input>,
) {
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.post<RepositoryResponse<Data>>(["entity", entity as any], input);
return this.post<RepositoryResultJSON<Data>>(["entity", entity as any], input);
}
createMany<E extends keyof DB | string, Input = E extends keyof DB ? DB[E] : EntityData>(
@@ -94,7 +91,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
throw new Error("input is required");
}
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.post<RepositoryResponse<Data[]>>(["entity", entity as any], input);
return this.post<RepositoryResultJSON<Data[]>>(["entity", entity as any], input);
}
updateOne<E extends keyof DB | string, Input = E extends keyof DB ? DB[E] : EntityData>(
@@ -104,7 +101,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
) {
if (!id) throw new Error("ID is required");
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.patch<RepositoryResponse<Data>>(["entity", entity as any, id], input);
return this.patch<RepositoryResultJSON<Data>>(["entity", entity as any, id], input);
}
updateMany<E extends keyof DB | string, Input = E extends keyof DB ? DB[E] : EntityData>(
@@ -114,7 +111,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
) {
this.requireObjectSet(where);
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.patch<RepositoryResponse<Data[]>>(["entity", entity as any], {
return this.patch<RepositoryResultJSON<Data[]>>(["entity", entity as any], {
update,
where,
});
@@ -123,24 +120,24 @@ export class DataApi extends ModuleApi<DataApiOptions> {
deleteOne<E extends keyof DB | string>(entity: E, id: PrimaryFieldType) {
if (!id) throw new Error("ID is required");
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.delete<RepositoryResponse<Data>>(["entity", entity as any, id]);
return this.delete<RepositoryResultJSON<Data>>(["entity", entity as any, id]);
}
deleteMany<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"]) {
this.requireObjectSet(where);
type Data = E extends keyof DB ? Selectable<DB[E]> : EntityData;
return this.delete<RepositoryResponse<Data>>(["entity", entity as any], where);
return this.delete<RepositoryResultJSON<Data>>(["entity", entity as any], where);
}
count<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"] = {}) {
return this.post<RepositoryResponse<{ entity: E; count: number }>>(
return this.post<RepositoryResultJSON<{ entity: E; count: number }>>(
["entity", entity as any, "fn", "count"],
where,
);
}
exists<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"] = {}) {
return this.post<RepositoryResponse<{ entity: E; exists: boolean }>>(
return this.post<RepositoryResultJSON<{ entity: E; exists: boolean }>>(
["entity", entity as any, "fn", "exists"],
where,
);

View File

@@ -1,6 +1,8 @@
import { Connection, type FieldSpec, type SchemaResponse } from "./Connection";
export class DummyConnection extends Connection {
override name: string = "dummy";
protected override readonly supported = {
batching: true,
};

View File

@@ -5,11 +5,13 @@ export {
type IndexSpec,
type DbFunctions,
type SchemaResponse,
type ConnQuery,
type ConnQueryResults,
customIntrospector,
} from "./Connection";
// sqlite
export { LibsqlConnection, type LibSqlCredentials } from "./sqlite/LibsqlConnection";
//export { libsql, LibsqlConnection, type LibSqlCredentials } from "./sqlite/LibsqlConnection";
export { SqliteConnection } from "./sqlite/SqliteConnection";
export { SqliteIntrospector } from "./sqlite/SqliteIntrospector";
export { SqliteLocalConnection } from "./sqlite/SqliteLocalConnection";

View File

@@ -1,15 +1,29 @@
import { createClient, type Client, type Config, type InStatement } from "@libsql/client";
import type { Client, Config, InStatement } from "@libsql/client";
import { createClient } from "libsql-stateless-easy";
import { LibsqlDialect } from "@libsql/kysely-libsql";
import { $console } from "core";
import { FilterNumericKeysPlugin } from "data/plugins/FilterNumericKeysPlugin";
import type { ConnQuery, ConnQueryResults } from "../Connection";
import { SqliteConnection } from "./SqliteConnection";
import { type ConnQuery, type ConnQueryResults, SqliteConnection } from "bknd/data";
export const LIBSQL_PROTOCOLS = ["wss", "https", "libsql"] as const;
export type LibSqlCredentials = Config & {
protocol?: (typeof LIBSQL_PROTOCOLS)[number];
};
function getClient(clientOrCredentials: Client | LibSqlCredentials): Client {
if (clientOrCredentials && "url" in clientOrCredentials) {
let { url, authToken, protocol } = clientOrCredentials;
if (protocol && LIBSQL_PROTOCOLS.includes(protocol)) {
console.info("changing protocol to", protocol);
const [, rest] = url.split("://");
url = `${protocol}://${rest}`;
}
return createClient({ url, authToken });
}
return clientOrCredentials as Client;
}
export class LibsqlConnection extends SqliteConnection<Client> {
override name = "libsql";
protected override readonly supported = {
@@ -17,22 +31,8 @@ export class LibsqlConnection extends SqliteConnection<Client> {
softscans: true,
};
constructor(client: Client);
constructor(credentials: LibSqlCredentials);
constructor(clientOrCredentials: Client | LibSqlCredentials) {
let client: Client;
if (clientOrCredentials && "url" in clientOrCredentials) {
let { url, authToken, protocol } = clientOrCredentials;
if (protocol && LIBSQL_PROTOCOLS.includes(protocol)) {
$console.log("changing protocol to", protocol);
const [, rest] = url.split("://");
url = `${protocol}://${rest}`;
}
client = createClient({ url, authToken });
} else {
client = clientOrCredentials;
}
const client = getClient(clientOrCredentials);
super({
excludeTables: ["libsql_wasm_func_table"],
@@ -56,3 +56,7 @@ export class LibsqlConnection extends SqliteConnection<Client> {
return this.withTransformedRows(await this.client.batch(stms)) as any;
}
}
export function libsql(credentials: LibSqlCredentials): LibsqlConnection {
return new LibsqlConnection(credentials);
}

View File

@@ -4,3 +4,5 @@ export * from "./mutation/Mutator";
export * from "./query/Repository";
export * from "./query/WhereBuilder";
export * from "./query/WithBuilder";
export * from "./query/RepositoryResult";
export * from "./mutation/MutatorResult";

View File

@@ -19,7 +19,7 @@ export class MutatorResult<T = EntityData[]> extends Result<T> {
hydrator: (rows) => em.hydrate(entity.name, rows as any),
beforeExecute: (compiled) => {
if (!options?.silent) {
$console.debug(`[Mutation]\n${compiled.sql}\n`, compiled.parameters);
$console.debug(`[Mutation]\n${compiled.sql}\n`);
}
},
onError: (error) => {

View File

@@ -216,7 +216,9 @@ export class MediaController extends Controller {
const paths_to_delete: string[] = [];
if (max_items) {
const { overwrite } = c.req.valid("query");
const { count } = await this.media.em.repository(media_entity).count(mediaRef);
const {
data: { count },
} = await this.media.em.repository(media_entity).count(mediaRef);
// if there are more than or equal to max items
if (count >= max_items) {
@@ -255,7 +257,9 @@ export class MediaController extends Controller {
}
// check if entity exists in database
const { exists } = await this.media.em.repository(entity).exists({ id: entity_id });
const {
data: { exists },
} = await this.media.em.repository(entity).exists({ id: entity_id });
if (!exists) {
return c.json(
{ error: `Entity "${entity_name}" with ID "${entity_id}" doesn't exist found` },

View File

@@ -234,7 +234,8 @@ export class ModuleManager {
}
private get db() {
return this.connection.kysely as Kysely<{ table: ConfigTable }>;
// @todo: check why this is neccessary
return this.connection.kysely as unknown as Kysely<{ table: ConfigTable }>;
}
// @todo: add indices for: version, type

View File

@@ -1,7 +1,7 @@
import type { DB, PrimaryFieldType } from "core";
import { objectTransform } from "core/utils/objects";
import { encodeSearch } from "core/utils/reqres";
import type { EntityData, RepoQueryIn, RepositoryResponse } from "data";
import type { EntityData, RepoQueryIn, RepositoryResult } from "data";
import type { Insertable, Selectable, Updateable } from "kysely";
import type { FetchPromise, ModuleApi, ResponseObject } from "modules/ModuleApi";
import useSWR, { type SWRConfiguration, type SWRResponse, mutate } from "swr";
@@ -28,15 +28,13 @@ interface UseEntityReturn<
Entity extends keyof DB | string,
Id extends PrimaryFieldType | undefined,
Data = Entity extends keyof DB ? DB[Entity] : EntityData,
Response = ResponseObject<RepositoryResponse<Selectable<Data>>>,
Response = ResponseObject<RepositoryResult<Selectable<Data>>>,
> {
create: (input: Insertable<Data>) => Promise<Response>;
read: (
query?: RepoQueryIn,
) => Promise<
ResponseObject<
RepositoryResponse<Id extends undefined ? Selectable<Data>[] : Selectable<Data>>
>
ResponseObject<RepositoryResult<Id extends undefined ? Selectable<Data>[] : Selectable<Data>>>
>;
update: Id extends undefined
? (input: Updateable<Data>, id: Id) => Promise<Response>

View File

@@ -3,30 +3,33 @@ import { serveStatic } from "@hono/node-server/serve-static";
import { showRoutes } from "hono/dev";
import { App, registries } from "./src";
import { StorageLocalAdapter } from "./src/adapter/node";
import { EntityManager, LibsqlConnection } from "data";
import type { Connection } from "./src/data/connection/Connection";
import { __bknd } from "modules/ModuleManager";
import { nodeSqlite } from "./src/adapter/node/connection/NodeSqliteConnection";
import { libsql } from "./src/data/connection/sqlite/LibsqlConnection";
import { $console } from "core";
//import { DatabaseSync } from "node:sqlite";
//import { nodeSqlite } from "./src/adapter/node/connection/NodeSqliteConnection";
registries.media.register("local", StorageLocalAdapter);
const example = import.meta.env.VITE_EXAMPLE;
const dbUrl = example ? `file:.configs/${example}.db` : import.meta.env.VITE_DB_URL;
const credentials = example
? {
url: `file:.configs/${example}.db`,
}
: import.meta.env.VITE_DB_URL
? {
url: import.meta.env.VITE_DB_URL!,
authToken: import.meta.env.VITE_DB_TOKEN!,
}
: {
url: ":memory:",
};
let connection: Connection;
if (dbUrl) {
connection = nodeSqlite({ url: dbUrl });
$console.debug("Using node-sqlite connection", dbUrl);
} else if (import.meta.env.VITE_DB_LIBSQL_URL) {
connection = libsql({
url: import.meta.env.VITE_DB_LIBSQL_URL!,
authToken: import.meta.env.VITE_DB_LIBSQL_TOKEN!,
});
$console.debug("Using libsql connection", import.meta.env.VITE_DB_URL);
} else {
connection = nodeSqlite();
$console.debug("No connection provided, using in-memory database");
}
if (example) {
/* if (example) {
const { version, ...config } = JSON.parse(await readFile(`.configs/${example}.json`, "utf-8"));
// create db with config
@@ -50,7 +53,7 @@ if (example) {
json: config,
});
}
}
} */
let app: App;
const recreate = import.meta.env.VITE_APP_FRESH === "1";
@@ -60,8 +63,7 @@ export default {
async fetch(request: Request) {
if (!app || recreate) {
app = App.create({
connection: credentials,
//connection: nodeSqlite({ database: new DatabaseSync(":memory:") }),
connection,
});
app.emgr.onEvent(
App.Events.AppBuiltEvent,
@@ -78,15 +80,11 @@ export default {
// log routes
if (firstStart) {
firstStart = false;
// biome-ignore lint/suspicious/noConsoleLog:
console.log("[DB]", credentials);
if (import.meta.env.VITE_SHOW_ROUTES === "1") {
// biome-ignore lint/suspicious/noConsoleLog:
console.log("\n[APP ROUTES]");
console.info("\n[APP ROUTES]");
showRoutes(app.server);
// biome-ignore lint/suspicious/noConsoleLog:
console.log("-------\n");
console.info("-------\n");
}
}
}