update & fix typing, updated examples

This commit is contained in:
dswbx
2024-12-23 19:28:31 +01:00
parent 70e42a02d7
commit c1e92e503b
21 changed files with 126 additions and 139 deletions

View File

@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
"bin": "./dist/cli/index.js", "bin": "./dist/cli/index.js",
"version": "0.3.4-alpha1", "version": "0.4.0-rc1",
"scripts": { "scripts": {
"build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli",
"dev": "vite", "dev": "vite",

View File

@@ -38,7 +38,7 @@ export class Api {
private token_transport: "header" | "cookie" | "none" = "header"; private token_transport: "header" | "cookie" | "none" = "header";
public system!: SystemApi; public system!: SystemApi;
public data!: DataApi<DB>; public data!: DataApi;
public auth!: AuthApi; public auth!: AuthApi;
public media!: MediaApi; public media!: MediaApi;

View File

@@ -10,7 +10,7 @@ import * as SystemPermissions from "modules/permissions";
import { AdminController, type AdminControllerOptions } from "modules/server/AdminController"; import { AdminController, type AdminControllerOptions } from "modules/server/AdminController";
import { SystemController } from "modules/server/SystemController"; import { SystemController } from "modules/server/SystemController";
export type AppPlugin<DB> = (app: App<DB>) => void; export type AppPlugin = (app: App) => void;
abstract class AppEvent<A = {}> extends Event<{ app: App } & A> {} abstract class AppEvent<A = {}> extends Event<{ app: App } & A> {}
export class AppConfigUpdatedEvent extends AppEvent { export class AppConfigUpdatedEvent extends AppEvent {
@@ -32,13 +32,13 @@ export type CreateAppConfig = {
config: LibSqlCredentials; config: LibSqlCredentials;
}; };
initialConfig?: InitialModuleConfigs; initialConfig?: InitialModuleConfigs;
plugins?: AppPlugin<any>[]; plugins?: AppPlugin[];
options?: Omit<ModuleManagerOptions, "initial" | "onUpdated">; options?: Omit<ModuleManagerOptions, "initial" | "onUpdated">;
}; };
export type AppConfig = InitialModuleConfigs; export type AppConfig = InitialModuleConfigs;
export class App<DB = any> { export class App {
modules: ModuleManager; modules: ModuleManager;
static readonly Events = AppEvents; static readonly Events = AppEvents;
adminController?: AdminController; adminController?: AdminController;
@@ -47,7 +47,7 @@ export class App<DB = any> {
constructor( constructor(
private connection: Connection, private connection: Connection,
_initialConfig?: InitialModuleConfigs, _initialConfig?: InitialModuleConfigs,
private plugins: AppPlugin<DB>[] = [], private plugins: AppPlugin[] = [],
moduleManagerOptions?: ModuleManagerOptions moduleManagerOptions?: ModuleManagerOptions
) { ) {
this.modules = new ModuleManager(connection, { this.modules = new ModuleManager(connection, {

View File

@@ -1,6 +1,6 @@
import { type AuthAction, Authenticator, type ProfileExchange, Role, type Strategy } from "auth"; import { type AuthAction, Authenticator, type ProfileExchange, Role, type Strategy } from "auth";
import type { PasswordStrategy } from "auth/authenticate/strategies"; import type { PasswordStrategy } from "auth/authenticate/strategies";
import { Exception } from "core"; import { Exception, type PrimaryFieldType } from "core";
import { type Static, secureRandomString, transformObject } from "core/utils"; import { type Static, secureRandomString, transformObject } from "core/utils";
import { type Entity, EntityIndex, type EntityManager } from "data"; import { type Entity, EntityIndex, type EntityManager } from "data";
import { type FieldSchema, entity, enumm, make, text } from "data/prototype"; import { type FieldSchema, entity, enumm, make, text } from "data/prototype";
@@ -10,9 +10,9 @@ import { AuthController } from "./api/AuthController";
import { type AppAuthSchema, STRATEGIES, authConfigSchema } from "./auth-schema"; import { type AppAuthSchema, STRATEGIES, authConfigSchema } from "./auth-schema";
export type UserFieldSchema = FieldSchema<typeof AppAuth.usersFields>; export type UserFieldSchema = FieldSchema<typeof AppAuth.usersFields>;
declare global { declare module "core" {
interface DB { interface DB {
users: UserFieldSchema; users: { id: PrimaryFieldType } & UserFieldSchema;
} }
} }
@@ -101,7 +101,7 @@ export class AppAuth extends Module<typeof authConfigSchema> {
return this._authenticator!; return this._authenticator!;
} }
get em(): EntityManager<DB> { get em(): EntityManager {
return this.ctx.em as any; return this.ctx.em as any;
} }
@@ -161,7 +161,9 @@ export class AppAuth extends Module<typeof authConfigSchema> {
const users = this.getUsersEntity(); const users = this.getUsersEntity();
this.toggleStrategyValueVisibility(true); this.toggleStrategyValueVisibility(true);
const result = await this.em.repo(users).findOne({ email: profile.email! }); const result = await this.em
.repo(users as unknown as "users")
.findOne({ email: profile.email! });
this.toggleStrategyValueVisibility(false); this.toggleStrategyValueVisibility(false);
if (!result.data) { if (!result.data) {
throw new Exception("User not found", 404); throw new Exception("User not found", 404);

View File

@@ -1,6 +1,7 @@
import { password as $password, text as $text } from "@clack/prompts"; import { password as $password, text as $text } from "@clack/prompts";
import type { App } from "App";
import type { BkndConfig } from "adapter";
import type { PasswordStrategy } from "auth/authenticate/strategies"; import type { PasswordStrategy } from "auth/authenticate/strategies";
import type { App, BkndConfig } from "bknd";
import { makeConfigApp } from "cli/commands/run"; import { makeConfigApp } from "cli/commands/run";
import { getConfigPath } from "cli/commands/run/platform"; import { getConfigPath } from "cli/commands/run/platform";
import type { CliCommand } from "cli/types"; import type { CliCommand } from "cli/types";
@@ -37,7 +38,7 @@ async function action(action: "create" | "update", options: any) {
async function create(app: App, options: any) { async function create(app: App, options: any) {
const config = app.module.auth.toJSON(true); const config = app.module.auth.toJSON(true);
const strategy = app.module.auth.authenticator.strategy("password") as PasswordStrategy; const strategy = app.module.auth.authenticator.strategy("password") as PasswordStrategy;
const users_entity = config.entity_name; const users_entity = config.entity_name as "users";
const email = await $text({ const email = await $text({
message: "Enter email", message: "Enter email",
@@ -83,7 +84,7 @@ async function create(app: App, options: any) {
async function update(app: App, options: any) { async function update(app: App, options: any) {
const config = app.module.auth.toJSON(true); const config = app.module.auth.toJSON(true);
const strategy = app.module.auth.authenticator.strategy("password") as PasswordStrategy; const strategy = app.module.auth.authenticator.strategy("password") as PasswordStrategy;
const users_entity = config.entity_name; const users_entity = config.entity_name as "users";
const em = app.modules.ctx().em; const em = app.modules.ctx().em;
const email = (await $text({ const email = (await $text({

View File

@@ -5,6 +5,9 @@ import type { Generated } from "kysely";
export type PrimaryFieldType = number | Generated<number>; export type PrimaryFieldType = number | Generated<number>;
// biome-ignore lint/suspicious/noEmptyInterface: <explanation>
export interface DB {}
export const config = { export const config = {
data: { data: {
default_primary_field: "id" default_primary_field: "id"

View File

@@ -3,7 +3,7 @@ import type { Hono, MiddlewareHandler } from "hono";
export { tbValidator } from "./server/lib/tbValidator"; export { tbValidator } from "./server/lib/tbValidator";
export { Exception, BkndError } from "./errors"; export { Exception, BkndError } from "./errors";
export { isDebug } from "./env"; export { isDebug } from "./env";
export { type PrimaryFieldType, config } from "./config"; export { type PrimaryFieldType, config, type DB } from "./config";
export { AwsClient } from "./clients/aws/AwsClient"; export { AwsClient } from "./clients/aws/AwsClient";
export { export {
SimpleRenderer, SimpleRenderer,

View File

@@ -11,7 +11,7 @@ import { Module } from "modules/Module";
import { DataController } from "./api/DataController"; import { DataController } from "./api/DataController";
import { type AppDataConfig, dataConfigSchema } from "./data-schema"; import { type AppDataConfig, dataConfigSchema } from "./data-schema";
export class AppData<DB> extends Module<typeof dataConfigSchema> { export class AppData extends Module<typeof dataConfigSchema> {
override async build() { override async build() {
const entities = transformObject(this.config.entities ?? {}, (entityConfig, name) => { const entities = transformObject(this.config.entities ?? {}, (entityConfig, name) => {
return constructEntity(name, entityConfig); return constructEntity(name, entityConfig);
@@ -59,7 +59,7 @@ export class AppData<DB> extends Module<typeof dataConfigSchema> {
return dataConfigSchema; return dataConfigSchema;
} }
get em(): EntityManager<DB> { get em(): EntityManager {
this.throwIfNotBuilt(); this.throwIfNotBuilt();
return this.ctx.em; return this.ctx.em;
} }

View File

@@ -1,3 +1,4 @@
import type { DB } from "core";
import type { EntityData, RepoQuery, RepositoryResponse } from "data"; import type { EntityData, RepoQuery, RepositoryResponse } from "data";
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules"; import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules";
@@ -5,7 +6,7 @@ export type DataApiOptions = BaseModuleApiOptions & {
defaultQuery?: Partial<RepoQuery>; defaultQuery?: Partial<RepoQuery>;
}; };
export class DataApi<DB> extends ModuleApi<DataApiOptions> { export class DataApi extends ModuleApi<DataApiOptions> {
protected override getDefaultOptions(): Partial<DataApiOptions> { protected override getDefaultOptions(): Partial<DataApiOptions> {
return { return {
basepath: "/api/data", basepath: "/api/data",

View File

@@ -1,3 +1,4 @@
import type { DB as DefaultDB } from "core";
import { EventManager } from "core/events"; import { EventManager } from "core/events";
import { sql } from "kysely"; import { sql } from "kysely";
import { Connection } from "../connection/Connection"; import { Connection } from "../connection/Connection";
@@ -14,15 +15,18 @@ import { SchemaManager } from "../schema/SchemaManager";
import { Entity } from "./Entity"; import { Entity } from "./Entity";
import { type EntityData, Mutator, Repository } from "./index"; import { type EntityData, Mutator, Repository } from "./index";
type EntitySchema<E extends Entity | string, DB = any> = E extends Entity<infer Name> type EntitySchema<
? Name extends keyof DB TBD extends object = DefaultDB,
E extends Entity | keyof TBD | string = string
> = E extends Entity<infer Name>
? Name extends keyof TBD
? Name ? Name
: never : never
: E extends keyof DB : E extends keyof TBD
? E ? E
: never; : never;
export class EntityManager<DB> { export class EntityManager<TBD extends object = DefaultDB> {
connection: Connection; connection: Connection;
private _entities: Entity[] = []; private _entities: Entity[] = [];
@@ -58,7 +62,7 @@ export class EntityManager<DB> {
* Forks the EntityManager without the EventManager. * Forks the EntityManager without the EventManager.
* This is useful when used inside an event handler. * This is useful when used inside an event handler.
*/ */
fork(): EntityManager<DB> { fork(): EntityManager {
return new EntityManager(this._entities, this.connection, this._relations, this._indices); return new EntityManager(this._entities, this.connection, this._relations, this._indices);
} }
@@ -95,16 +99,17 @@ export class EntityManager<DB> {
this.entities.push(entity); this.entities.push(entity);
} }
entity(e: Entity | string): Entity { entity(e: Entity | keyof TBD | string): Entity {
let entity: Entity | undefined; let entity: Entity | undefined;
if (typeof e === "string") { if (typeof e === "string") {
entity = this.entities.find((entity) => entity.name === e); entity = this.entities.find((entity) => entity.name === e);
} else { } else if (e instanceof Entity) {
entity = e; entity = e;
} }
if (!entity) { if (!entity) {
throw new EntityNotDefinedException(typeof e === "string" ? e : e.name); // @ts-ignore
throw new EntityNotDefinedException(e instanceof Entity ? e.name : e);
} }
return entity; return entity;
@@ -176,15 +181,17 @@ export class EntityManager<DB> {
return this.relations.relationReferencesOf(this.entity(entity_name)); return this.relations.relationReferencesOf(this.entity(entity_name));
} }
repository<E extends Entity | string>(entity: E): Repository<DB, EntitySchema<E, DB>> { repository<E extends Entity | keyof TBD | string>(
entity: E
): Repository<TBD, EntitySchema<TBD, E>> {
return this.repo(entity); return this.repo(entity);
} }
repo<E extends Entity | string>(entity: E): Repository<DB, EntitySchema<E, DB>> { repo<E extends Entity | keyof TBD | string>(entity: E): Repository<TBD, EntitySchema<TBD, E>> {
return new Repository(this, this.entity(entity), this.emgr); return new Repository(this, this.entity(entity), this.emgr);
} }
mutator<E extends Entity | string>(entity: E): Mutator<DB, EntitySchema<E, DB>> { mutator<E extends Entity | keyof TBD | string>(entity: E): Mutator<TBD, EntitySchema<TBD, E>> {
return new Mutator(this, this.entity(entity), this.emgr); return new Mutator(this, this.entity(entity), this.emgr);
} }

View File

@@ -1,4 +1,4 @@
import type { PrimaryFieldType } from "core"; import type { DB as DefaultDB, PrimaryFieldType } from "core";
import { type EmitsEvents, EventManager } from "core/events"; import { type EmitsEvents, EventManager } from "core/events";
import type { DeleteQueryBuilder, InsertQueryBuilder, UpdateQueryBuilder } from "kysely"; import type { DeleteQueryBuilder, InsertQueryBuilder, UpdateQueryBuilder } from "kysely";
import { type TActionContext, WhereBuilder } from ".."; import { type TActionContext, WhereBuilder } from "..";
@@ -26,13 +26,13 @@ export type MutatorResponse<T = EntityData[]> = {
}; };
export class Mutator< export class Mutator<
DB = any, TBD extends object = DefaultDB,
TB extends keyof DB = any, TB extends keyof TBD = any,
Output = DB[TB], Output = TBD[TB],
Input = Omit<Output, "id"> Input = Omit<Output, "id">
> implements EmitsEvents > implements EmitsEvents
{ {
em: EntityManager<DB>; em: EntityManager<TBD>;
entity: Entity; entity: Entity;
static readonly Events = MutatorEvents; static readonly Events = MutatorEvents;
emgr: EventManager<typeof MutatorEvents>; emgr: EventManager<typeof MutatorEvents>;
@@ -43,7 +43,7 @@ export class Mutator<
this.__unstable_disable_system_entity_creation = value; this.__unstable_disable_system_entity_creation = value;
} }
constructor(em: EntityManager<DB>, entity: Entity, emgr?: EventManager<any>) { constructor(em: EntityManager<TBD>, entity: Entity, emgr?: EventManager<any>) {
this.em = em; this.em = em;
this.entity = entity; this.entity = entity;
this.emgr = emgr ?? new EventManager(MutatorEvents); this.emgr = emgr ?? new EventManager(MutatorEvents);
@@ -163,7 +163,7 @@ export class Mutator<
return res as any; return res as any;
} }
async updateOne(id: PrimaryFieldType, data: Input): Promise<MutatorResponse<Output>> { async updateOne(id: PrimaryFieldType, data: Partial<Input>): Promise<MutatorResponse<Output>> {
const entity = this.entity; const entity = this.entity;
if (!Number.isInteger(id)) { if (!Number.isInteger(id)) {
throw new Error("ID must be provided for update"); throw new Error("ID must be provided for update");
@@ -270,7 +270,10 @@ export class Mutator<
return (await this.many(qb)) as any; return (await this.many(qb)) as any;
} }
async updateWhere(data: Partial<Input>, where?: RepoQuery["where"]): Promise<MutatorResponse<Output[]>> { async updateWhere(
data: Partial<Input>,
where?: RepoQuery["where"]
): Promise<MutatorResponse<Output[]>> {
const entity = this.entity; const entity = this.entity;
const validatedData = await this.getValidatedData(data, "update"); const validatedData = await this.getValidatedData(data, "update");

View File

@@ -1,4 +1,4 @@
import type { PrimaryFieldType } from "core"; import type { DB as DefaultDB, PrimaryFieldType } from "core";
import { type EmitsEvents, EventManager } from "core/events"; import { type EmitsEvents, EventManager } from "core/events";
import { type SelectQueryBuilder, sql } from "kysely"; import { type SelectQueryBuilder, sql } from "kysely";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
@@ -43,13 +43,15 @@ export type RepositoryExistsResponse = RepositoryRawResponse & {
exists: boolean; exists: boolean;
}; };
export class Repository<DB = any, TB extends keyof DB = any> implements EmitsEvents { export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = any>
em: EntityManager<DB>; implements EmitsEvents
{
em: EntityManager<TBD>;
entity: Entity; entity: Entity;
static readonly Events = RepositoryEvents; static readonly Events = RepositoryEvents;
emgr: EventManager<typeof Repository.Events>; emgr: EventManager<typeof Repository.Events>;
constructor(em: EntityManager<DB>, entity: Entity, emgr?: EventManager<any>) { constructor(em: EntityManager<TBD>, entity: Entity, emgr?: EventManager<any>) {
this.em = em; this.em = em;
this.entity = entity; this.entity = entity;
this.emgr = emgr ?? new EventManager(MutatorEvents); this.emgr = emgr ?? new EventManager(MutatorEvents);
@@ -272,7 +274,7 @@ export class Repository<DB = any, TB extends keyof DB = any> implements EmitsEve
async findId( async findId(
id: PrimaryFieldType, id: PrimaryFieldType,
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">> _options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
): Promise<RepositoryResponse<DB[TB] | undefined>> { ): Promise<RepositoryResponse<TBD[TB] | undefined>> {
const { qb, options } = this.buildQuery( const { qb, options } = this.buildQuery(
{ {
..._options, ..._options,
@@ -288,7 +290,7 @@ export class Repository<DB = any, TB extends keyof DB = any> implements EmitsEve
async findOne( async findOne(
where: RepoQuery["where"], where: RepoQuery["where"],
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">> _options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
): Promise<RepositoryResponse<DB[TB] | undefined>> { ): Promise<RepositoryResponse<TBD[TB] | undefined>> {
const { qb, options } = this.buildQuery({ const { qb, options } = this.buildQuery({
..._options, ..._options,
where, where,
@@ -298,7 +300,7 @@ export class Repository<DB = any, TB extends keyof DB = any> implements EmitsEve
return this.single(qb, options) as any; return this.single(qb, options) as any;
} }
async findMany(_options?: Partial<RepoQuery>): Promise<RepositoryResponse<DB[TB][]>> { async findMany(_options?: Partial<RepoQuery>): Promise<RepositoryResponse<TBD[TB][]>> {
const { qb, options } = this.buildQuery(_options); const { qb, options } = this.buildQuery(_options);
//console.log("findMany:options", options); //console.log("findMany:options", options);

View File

@@ -1,24 +1,15 @@
import type { PrimaryFieldType } from "core";
import { EntityIndex, type EntityManager } from "data"; import { EntityIndex, type EntityManager } from "data";
import { type FileUploadedEventData, Storage, type StorageAdapter } from "media"; import { type FileUploadedEventData, Storage, type StorageAdapter } from "media";
import { Module } from "modules/Module"; import { Module } from "modules/Module";
import { import { type FieldSchema, boolean, datetime, entity, json, number, text } from "../data/prototype";
type FieldSchema,
type InferFields,
type Schema,
boolean,
datetime,
entity,
json,
number,
text
} from "../data/prototype";
import { MediaController } from "./api/MediaController"; import { MediaController } from "./api/MediaController";
import { ADAPTERS, buildMediaSchema, type mediaConfigSchema, registry } from "./media-schema"; import { ADAPTERS, buildMediaSchema, type mediaConfigSchema, registry } from "./media-schema";
export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>; export type MediaFieldSchema = FieldSchema<typeof AppMedia.mediaFields>;
declare global { declare module "core" {
interface DB { interface DB {
media: MediaFieldSchema; media: { id: PrimaryFieldType } & MediaFieldSchema;
} }
} }
@@ -112,14 +103,14 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
return this.em.entity(entity_name); return this.em.entity(entity_name);
} }
get em(): EntityManager<DB> { get em(): EntityManager {
return this.ctx.em; return this.ctx.em;
} }
private setupListeners() { private setupListeners() {
//const media = this._entity; //const media = this._entity;
const { emgr, em } = this.ctx; const { emgr, em } = this.ctx;
const media = this.getMediaEntity(); const media = this.getMediaEntity().name as "media";
// when file is uploaded, sync with media entity // when file is uploaded, sync with media entity
// @todo: need a way for singleton events! // @todo: need a way for singleton events!
@@ -140,10 +131,10 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
Storage.Events.FileDeletedEvent, Storage.Events.FileDeletedEvent,
async (e) => { async (e) => {
// simple file deletion sync // simple file deletion sync
const item = await em.repo(media).findOne({ path: e.params.name }); const { data } = await em.repo(media).findOne({ path: e.params.name });
if (item.data) { if (data) {
console.log("item.data", item.data); console.log("item.data", data);
await em.mutator(media).deleteOne(item.data.id); await em.mutator(media).deleteOne(data.id);
} }
console.log("App:storage:file deleted", e); console.log("App:storage:file deleted", e);

View File

@@ -5,10 +5,10 @@ import type { Static, TSchema } from "core/utils";
import type { Connection, EntityManager } from "data"; import type { Connection, EntityManager } from "data";
import type { Hono } from "hono"; import type { Hono } from "hono";
export type ModuleBuildContext<DB = any> = { export type ModuleBuildContext = {
connection: Connection; connection: Connection;
server: Hono<any>; server: Hono<any>;
em: EntityManager<DB>; em: EntityManager;
emgr: EventManager<any>; emgr: EventManager<any>;
guard: Guard; guard: Guard;
}; };

View File

@@ -1,5 +1,5 @@
import { Guard } from "auth"; import { Guard } from "auth";
import { BkndError, DebugLogger, Exception, isDebug } from "core"; import { BkndError, DebugLogger } from "core";
import { EventManager } from "core/events"; import { EventManager } from "core/events";
import { clone, diff } from "core/object/diff"; import { clone, diff } from "core/object/diff";
import { import {
@@ -39,7 +39,7 @@ export type { ModuleBuildContext };
export const MODULES = { export const MODULES = {
server: AppServer, server: AppServer,
data: AppData<any>, data: AppData,
auth: AppAuth, auth: AppAuth,
media: AppMedia, media: AppMedia,
flows: AppFlows flows: AppFlows
@@ -112,9 +112,9 @@ const __bknd = entity(TABLE_NAME, {
updated_at: datetime() updated_at: datetime()
}); });
type ConfigTable2 = Schema<typeof __bknd>; type ConfigTable2 = Schema<typeof __bknd>;
type T_INTERNAL_EM = { interface T_INTERNAL_EM {
__bknd: ConfigTable2; __bknd: ConfigTable2;
}; }
// @todo: cleanup old diffs on upgrade // @todo: cleanup old diffs on upgrade
// @todo: cleanup multiple backups on upgrade // @todo: cleanup multiple backups on upgrade
@@ -123,7 +123,7 @@ export class ModuleManager {
// internal em for __bknd config table // internal em for __bknd config table
__em!: EntityManager<T_INTERNAL_EM>; __em!: EntityManager<T_INTERNAL_EM>;
// ctx for modules // ctx for modules
em!: EntityManager<any>; em!: EntityManager;
server!: Hono; server!: Hono;
emgr!: EventManager; emgr!: EventManager;
guard!: Guard; guard!: Guard;

View File

@@ -1,37 +0,0 @@
import type { DataApi } from "data/api/DataApi";
import { useApi } from "ui/client";
type OmitFirstArg<F> = F extends (x: any, ...args: infer P) => any
? (...args: P) => ReturnType<F>
: never;
/**
* Maps all DataApi functions and omits
* the first argument "entity" for convenience
* @param entity
*/
export const useData = <T extends keyof DataApi<DB>>(entity: string) => {
const api = useApi().data;
const methods = [
"readOne",
"readMany",
"readManyByReference",
"createOne",
"updateOne",
"deleteOne"
] as const;
return methods.reduce(
(acc, method) => {
// @ts-ignore
acc[method] = (...params) => {
// @ts-ignore
return api[method](entity, ...params);
};
return acc;
},
{} as {
[K in (typeof methods)[number]]: OmitFirstArg<(typeof api)[K]>;
}
);
};

View File

@@ -1,4 +1,4 @@
import type { PrimaryFieldType } from "core"; import type { DB, PrimaryFieldType } from "core";
import { encodeSearch, objectTransform } from "core/utils"; import { encodeSearch, objectTransform } from "core/utils";
import type { EntityData, RepoQuery } from "data"; import type { EntityData, RepoQuery } from "data";
import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; import type { ModuleApi, ResponseObject } from "modules/ModuleApi";

View File

@@ -7,7 +7,6 @@ export {
} from "./ClientProvider"; } from "./ClientProvider";
export * from "./api/use-api"; export * from "./api/use-api";
export * from "./api/use-data";
export * from "./api/use-entity"; export * from "./api/use-entity";
export { useAuth } from "./schema/auth/use-auth"; export { useAuth } from "./schema/auth/use-auth";
export { Api } from "../../Api"; export { Api } from "../../Api";

View File

@@ -1,4 +1,4 @@
import { App } from "bknd"; import { Api, App } from "bknd";
import { serve } from "bknd/adapter/astro"; import { serve } from "bknd/adapter/astro";
import { registerLocalMediaAdapter } from "bknd/adapter/node"; import { registerLocalMediaAdapter } from "bknd/adapter/node";
import { boolean, em, entity, text } from "bknd/data"; import { boolean, em, entity, text } from "bknd/data";
@@ -9,6 +9,20 @@ export const prerender = false;
// since we're running in node, we can register the local media adapter // since we're running in node, we can register the local media adapter
registerLocalMediaAdapter(); registerLocalMediaAdapter();
// the em() function makes it easy to create an initial schema
const schema = em({
todos: entity("todos", {
title: text(),
done: boolean()
})
});
// register your schema to get automatic type completion
type Database = (typeof schema)["DB"];
declare module "bknd/core" {
interface DB extends Database {}
}
export const ALL = serve({ export const ALL = serve({
// we can use any libsql config, and if omitted, uses in-memory // we can use any libsql config, and if omitted, uses in-memory
connection: { connection: {
@@ -19,13 +33,7 @@ export const ALL = serve({
}, },
// an initial config is only applied if the database is empty // an initial config is only applied if the database is empty
initialConfig: { initialConfig: {
// the em() function makes it easy to create an initial schema data: schema.toJSON(),
data: em({
todos: entity("todos", {
title: text(),
done: boolean()
})
}).toJSON(),
// we're enabling auth ... // we're enabling auth ...
auth: { auth: {
enabled: true, enabled: true,

View File

@@ -7,6 +7,19 @@ import { secureRandomString } from "bknd/utils";
// since we're running in node, we can register the local media adapter // since we're running in node, we can register the local media adapter
registerLocalMediaAdapter(); registerLocalMediaAdapter();
const schema = em({
todos: entity("todos", {
title: text(),
done: boolean()
})
});
// register your schema to get automatic type completion
type Database = (typeof schema)["DB"];
declare module "bknd/core" {
interface DB extends Database {}
}
const handler = serve({ const handler = serve({
// we can use any libsql config, and if omitted, uses in-memory // we can use any libsql config, and if omitted, uses in-memory
connection: { connection: {
@@ -17,13 +30,7 @@ const handler = serve({
}, },
// an initial config is only applied if the database is empty // an initial config is only applied if the database is empty
initialConfig: { initialConfig: {
// the em() function makes it easy to create an initial schema data: schema.toJSON(),
data: em({
todos: entity("todos", {
title: text(),
done: boolean()
})
}).toJSON(),
// we're enabling auth ... // we're enabling auth ...
auth: { auth: {
enabled: true, enabled: true,

View File

@@ -16,9 +16,9 @@ export default defineConfig({
v3_relativeSplatPath: true, v3_relativeSplatPath: true,
v3_throwAbortReason: true, v3_throwAbortReason: true,
v3_singleFetch: true, v3_singleFetch: true,
v3_lazyRouteDiscovery: true, v3_lazyRouteDiscovery: true
}, }
}), }) as any,
tsconfigPaths(), tsconfigPaths()
], ]
}); });