diff --git a/app/package.json b/app/package.json index c451ce8..a5efc5c 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.4.0-rc1", + "version": "0.4.0-rc2", "scripts": { "build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "dev": "vite", diff --git a/app/src/adapter/astro/astro.adapter.ts b/app/src/adapter/astro/astro.adapter.ts index c11e95e..479b873 100644 --- a/app/src/adapter/astro/astro.adapter.ts +++ b/app/src/adapter/astro/astro.adapter.ts @@ -1,4 +1,7 @@ -import { Api, type ApiOptions, App, type CreateAppConfig } from "bknd"; +import { type FrameworkBkndConfig, createFrameworkApp } from "adapter"; +import { Api, type ApiOptions, type App } from "bknd"; + +export type AstroBkndConfig = FrameworkBkndConfig; type TAstro = { request: Request; @@ -18,12 +21,10 @@ export function getApi(Astro: TAstro, options: Options = { mode: "static" }) { } let app: App; -export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise }) { +export function serve(config: AstroBkndConfig = {}) { return async (args: TAstro) => { if (!app) { - app = App.create(config); - await config.beforeBuild?.(app); - await app.build(); + app = await createFrameworkApp(config); } return app.fetch(args.request); }; diff --git a/app/src/adapter/bun/bun.adapter.ts b/app/src/adapter/bun/bun.adapter.ts index 6ffcc6b..7e1334c 100644 --- a/app/src/adapter/bun/bun.adapter.ts +++ b/app/src/adapter/bun/bun.adapter.ts @@ -1,64 +1,47 @@ /// import path from "node:path"; -import { App, type CreateAppConfig, registries } from "bknd"; -import type { Serve, ServeOptions } from "bun"; +import type { App } from "bknd"; +import type { ServeOptions } from "bun"; +import { config } from "core"; import { serveStatic } from "hono/bun"; -import { registerLocalMediaAdapter } from "../index"; +import { type RuntimeBkndConfig, createRuntimeApp } from "../index"; let app: App; -export type ExtendedAppCreateConfig = Partial & { - distPath?: string; - onBuilt?: (app: App) => Promise; - buildOptions?: Parameters[0]; -}; + +export type BunBkndConfig = RuntimeBkndConfig & Omit; export async function createApp({ distPath, onBuilt, - buildOptions, + buildConfig, + beforeBuild, ...config -}: ExtendedAppCreateConfig) { - registerLocalMediaAdapter(); +}: RuntimeBkndConfig = {}) { const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static"); if (!app) { - app = App.create(config); - - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async () => { - app.modules.server.get( - "/*", - serveStatic({ - root - }) - ); - app.registerAdminController(); - await onBuilt?.(app); - }, - "sync" - ); - - await app.build(buildOptions); + app = await createRuntimeApp({ + ...config, + registerLocalMedia: true, + serveStatic: serveStatic({ root }) + }); } return app; } -export type BunAdapterOptions = Omit & ExtendedAppCreateConfig; - export function serve({ distPath, connection, initialConfig, plugins, options, - port = 1337, + port = config.server.default_port, onBuilt, - buildOptions, + buildConfig, ...serveOptions -}: BunAdapterOptions = {}) { +}: BunBkndConfig = {}) { Bun.serve({ ...serveOptions, port, @@ -69,7 +52,7 @@ export function serve({ plugins, options, onBuilt, - buildOptions, + buildConfig, distPath }); return app.fetch(request); diff --git a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts index 1603967..da3c762 100644 --- a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts +++ b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts @@ -1,22 +1,37 @@ +import type { CreateAppConfig } from "bknd"; import { Hono } from "hono"; import { serveStatic } from "hono/cloudflare-workers"; -import type { BkndConfig } from "../index"; +import type { FrameworkBkndConfig } from "../index"; import { getCached } from "./modes/cached"; import { getDurable } from "./modes/durable"; import { getFresh, getWarm } from "./modes/fresh"; +export type CloudflareBkndConfig = Omit & { + app: CreateAppConfig | ((env: Env) => CreateAppConfig); + mode?: "warm" | "fresh" | "cache" | "durable"; + bindings?: (env: Env) => { + kv?: KVNamespace; + dobj?: DurableObjectNamespace; + }; + key?: string; + keepAliveSeconds?: number; + forceHttps?: boolean; + manifest?: string; + setAdminHtml?: boolean; + html?: string; +}; + export type Context = { request: Request; env: any; ctx: ExecutionContext; - manifest: any; - html?: string; }; -export function serve(_config: BkndConfig, manifest?: string, html?: string) { +export function serve(config: CloudflareBkndConfig) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { const url = new URL(request.url); + const manifest = config.manifest; if (manifest) { const pathname = url.pathname.slice(1); @@ -27,8 +42,7 @@ export function serve(_config: BkndConfig, manifest?: string, html?: string) { hono.all("*", async (c, next) => { const res = await serveStatic({ path: `./${pathname}`, - manifest, - onNotFound: (path) => console.log("not found", path) + manifest })(c as any, next); if (res instanceof Response) { const ttl = 60 * 60 * 24 * 365; @@ -43,12 +57,10 @@ export function serve(_config: BkndConfig, manifest?: string, html?: string) { } } - const config = { - ..._config, - setAdminHtml: _config.setAdminHtml ?? !!manifest - }; - const context = { request, env, ctx, manifest, html } as Context; - const mode = config.cloudflare?.mode ?? "warm"; + config.setAdminHtml = config.setAdminHtml && !!config.manifest; + + const context = { request, env, ctx } as Context; + const mode = config.mode ?? "warm"; switch (mode) { case "fresh": diff --git a/app/src/adapter/cloudflare/modes/cached.ts b/app/src/adapter/cloudflare/modes/cached.ts index adcd5a3..c1be74e 100644 --- a/app/src/adapter/cloudflare/modes/cached.ts +++ b/app/src/adapter/cloudflare/modes/cached.ts @@ -1,51 +1,42 @@ -import type { BkndConfig } from "adapter"; +import { createRuntimeApp } from "adapter"; import { App } from "bknd"; -import type { Context } from "../index"; +import type { CloudflareBkndConfig, Context } from "../index"; -export async function getCached(config: BkndConfig, { env, html, ctx }: Context) { - const { kv } = config.cloudflare?.bindings?.(env)!; +export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Context) { + const { kv } = config.bindings?.(env)!; if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings"); - const key = config.cloudflare?.key ?? "app"; + const key = config.key ?? "app"; const create_config = typeof config.app === "function" ? config.app(env) : config.app; const cachedConfig = await kv.get(key); const initialConfig = cachedConfig ? JSON.parse(cachedConfig) : undefined; - const app = App.create({ ...create_config, initialConfig }); - async function saveConfig(__config: any) { ctx.waitUntil(kv!.put(key, JSON.stringify(__config))); } - if (config.onBuilt) { - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async ({ params: { app } }) => { - app.module.server.client.get("/__bknd/cache", async (c) => { - await kv.delete(key); - return c.json({ message: "Cache cleared" }); - }); - app.registerAdminController({ html }); - - config.onBuilt!(app); - }, - "sync" - ); - } - - app.emgr.onEvent( - App.Events.AppConfigUpdatedEvent, - async ({ params: { app } }) => { - saveConfig(app.toJSON(true)); + const app = await createRuntimeApp({ + ...create_config, + initialConfig, + onBuilt: async (app) => { + app.module.server.client.get("/__bknd/cache", async (c) => { + await kv.delete(key); + return c.json({ message: "Cache cleared" }); + }); + await config.onBuilt?.(app); }, - "sync" - ); - - await app.build(); - - if (config.setAdminHtml) { - app.registerAdminController({ html }); - } + beforeBuild: async (app) => { + app.emgr.onEvent( + App.Events.AppConfigUpdatedEvent, + async ({ params: { app } }) => { + saveConfig(app.toJSON(true)); + }, + "sync" + ); + await config.beforeBuild?.(app); + }, + adminOptions: { html: config.html } + }); if (!cachedConfig) { saveConfig(app.toJSON(true)); diff --git a/app/src/adapter/cloudflare/modes/durable.ts b/app/src/adapter/cloudflare/modes/durable.ts index 60f7a0e..3787b5c 100644 --- a/app/src/adapter/cloudflare/modes/durable.ts +++ b/app/src/adapter/cloudflare/modes/durable.ts @@ -1,15 +1,15 @@ import { DurableObject } from "cloudflare:workers"; -import type { BkndConfig } from "adapter"; -import type { Context } from "adapter/cloudflare"; -import { App, type CreateAppConfig } from "bknd"; +import { createRuntimeApp } from "adapter"; +import type { CloudflareBkndConfig, Context } from "adapter/cloudflare"; +import type { App, CreateAppConfig } from "bknd"; -export async function getDurable(config: BkndConfig, ctx: Context) { - const { dobj } = config.cloudflare?.bindings?.(ctx.env)!; +export async function getDurable(config: CloudflareBkndConfig, ctx: Context) { + const { dobj } = config.bindings?.(ctx.env)!; if (!dobj) throw new Error("durable object is not defined in cloudflare.bindings"); - const key = config.cloudflare?.key ?? "app"; + const key = config.key ?? "app"; - if (config.onBuilt) { - console.log("onBuilt() is not supported with DurableObject mode"); + if ([config.onBuilt, config.beforeBuild].some((x) => x)) { + console.log("onBuilt and beforeBuild are not supported with DurableObject mode"); } const start = performance.now(); @@ -21,8 +21,8 @@ export async function getDurable(config: BkndConfig, ctx: Context) { const res = await stub.fire(ctx.request, { config: create_config, - html: ctx.html, - keepAliveSeconds: config.cloudflare?.keepAliveSeconds, + html: config.html, + keepAliveSeconds: config.keepAliveSeconds, setAdminHtml: config.setAdminHtml }); @@ -64,10 +64,9 @@ export class DurableBkndApp extends DurableObject { config.connection.config.protocol = "wss"; } - this.app = App.create(config); - this.app.emgr.onEvent( - App.Events.AppBuiltEvent, - async ({ params: { app } }) => { + this.app = await createRuntimeApp({ + ...config, + onBuilt: async (app) => { app.modules.server.get("/__do", async (c) => { // @ts-ignore const context: any = c.req.raw.cf ? c.req.raw.cf : c.env.cf; @@ -80,14 +79,11 @@ export class DurableBkndApp extends DurableObject { await this.onBuilt(app); }, - "sync" - ); - - await this.app.build(); - - if (options.setAdminHtml) { - this.app.registerAdminController({ html: options.html }); - } + adminOptions: { html: options.html }, + beforeBuild: async (app) => { + await this.beforeBuild(app); + } + }); buildtime = performance.now() - start; } @@ -110,6 +106,7 @@ export class DurableBkndApp extends DurableObject { } async onBuilt(app: App) {} + async beforeBuild(app: App) {} protected keepAlive(seconds: number) { console.log("keep alive for", seconds); diff --git a/app/src/adapter/cloudflare/modes/fresh.ts b/app/src/adapter/cloudflare/modes/fresh.ts index 639a20d..b738931 100644 --- a/app/src/adapter/cloudflare/modes/fresh.ts +++ b/app/src/adapter/cloudflare/modes/fresh.ts @@ -1,36 +1,23 @@ -import type { BkndConfig } from "adapter"; -import { App } from "bknd"; -import type { Context } from "../index"; +import { createRuntimeApp } from "adapter"; +import type { App } from "bknd"; +import type { CloudflareBkndConfig, Context } from "../index"; -export async function makeApp(config: BkndConfig, { env, html }: Context) { +export async function makeApp(config: CloudflareBkndConfig, { env }: Context) { const create_config = typeof config.app === "function" ? config.app(env) : config.app; - const app = App.create(create_config); - - if (config.onBuilt) { - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async ({ params: { app } }) => { - config.onBuilt!(app); - }, - "sync" - ); - } - await app.build(); - - if (config.setAdminHtml) { - app.registerAdminController({ html }); - } - - return app; + return await createRuntimeApp({ + ...config, + ...create_config, + adminOptions: config.html ? { html: config.html } : undefined + }); } -export async function getFresh(config: BkndConfig, ctx: Context) { +export async function getFresh(config: CloudflareBkndConfig, ctx: Context) { const app = await makeApp(config, ctx); return app.fetch(ctx.request); } let warm_app: App; -export async function getWarm(config: BkndConfig, ctx: Context) { +export async function getWarm(config: CloudflareBkndConfig, ctx: Context) { if (!warm_app) { warm_app = await makeApp(config, ctx); } diff --git a/app/src/adapter/index.ts b/app/src/adapter/index.ts index 43019d7..b248037 100644 --- a/app/src/adapter/index.ts +++ b/app/src/adapter/index.ts @@ -1,28 +1,19 @@ import type { IncomingMessage } from "node:http"; -import { type App, type CreateAppConfig, registries } from "bknd"; +import { App, type CreateAppConfig, registries } from "bknd"; +import type { MiddlewareHandler } from "hono"; import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter"; +import type { AdminControllerOptions } from "modules/server/AdminController"; -export type CloudflareBkndConfig = { - mode?: "warm" | "fresh" | "cache" | "durable"; - bindings?: (env: Env) => { - kv?: KVNamespace; - dobj?: DurableObjectNamespace; - }; - key?: string; - keepAliveSeconds?: number; - forceHttps?: boolean; +type BaseExternalBkndConfig = CreateAppConfig & { + onBuilt?: (app: App) => Promise; + beforeBuild?: (app: App) => Promise; + buildConfig?: Parameters[0]; }; -// @todo: move to App -export type BkndConfig = { - app: CreateAppConfig | ((env: Env) => CreateAppConfig); - setAdminHtml?: boolean; - server?: { - port?: number; - platform?: "node" | "bun"; - }; - cloudflare?: CloudflareBkndConfig; - onBuilt?: (app: App) => Promise; +export type FrameworkBkndConfig = BaseExternalBkndConfig; + +export type RuntimeBkndConfig = BaseExternalBkndConfig & { + distPath?: string; }; export function nodeRequestToRequest(req: IncomingMessage): Request { @@ -52,3 +43,64 @@ export function nodeRequestToRequest(req: IncomingMessage): Request { export function registerLocalMediaAdapter() { registries.media.register("local", StorageLocalAdapter); } + +export async function createFrameworkApp(config: FrameworkBkndConfig): Promise { + const app = App.create(config); + + if (config.onBuilt) { + app.emgr.onEvent( + App.Events.AppBuiltEvent, + async () => { + await config.onBuilt?.(app); + }, + "sync" + ); + } + + await config.beforeBuild?.(app); + await app.build(config.buildConfig); + + return app; +} + +export async function createRuntimeApp({ + serveStatic, + registerLocalMedia, + adminOptions, + ...config +}: RuntimeBkndConfig & { + serveStatic?: MiddlewareHandler | [string, MiddlewareHandler]; + registerLocalMedia?: boolean; + adminOptions?: AdminControllerOptions | false; +}): Promise { + if (registerLocalMedia) { + registerLocalMediaAdapter(); + } + + const app = App.create(config); + + app.emgr.onEvent( + App.Events.AppBuiltEvent, + async () => { + if (serveStatic) { + if (Array.isArray(serveStatic)) { + const [path, handler] = serveStatic; + app.modules.server.get(path, handler); + } else { + app.modules.server.get("/*", serveStatic); + } + } + + await config.onBuilt?.(app); + if (adminOptions !== false) { + app.registerAdminController(adminOptions); + } + }, + "sync" + ); + + await config.beforeBuild?.(app); + await app.build(config.buildConfig); + + return app; +} diff --git a/app/src/adapter/nextjs/nextjs.adapter.ts b/app/src/adapter/nextjs/nextjs.adapter.ts index fe9197a..eaee4ab 100644 --- a/app/src/adapter/nextjs/nextjs.adapter.ts +++ b/app/src/adapter/nextjs/nextjs.adapter.ts @@ -1,6 +1,8 @@ import type { IncomingMessage, ServerResponse } from "node:http"; -import { Api, App, type CreateAppConfig } from "bknd"; -import { nodeRequestToRequest } from "../index"; +import { Api, type App } from "bknd"; +import { type FrameworkBkndConfig, createFrameworkApp, nodeRequestToRequest } from "../index"; + +export type NextjsBkndConfig = FrameworkBkndConfig; type GetServerSidePropsContext = { req: IncomingMessage; @@ -42,12 +44,10 @@ function getCleanRequest(req: Request) { } let app: App; -export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise }) { +export function serve(config: NextjsBkndConfig = {}) { return async (req: Request) => { if (!app) { - app = App.create(config); - await config.beforeBuild?.(app); - await app.build(); + app = await createFrameworkApp(config); } const request = getCleanRequest(req); return app.fetch(request, process.env); diff --git a/app/src/adapter/node/node.adapter.ts b/app/src/adapter/node/node.adapter.ts index d5fb196..835b886 100644 --- a/app/src/adapter/node/node.adapter.ts +++ b/app/src/adapter/node/node.adapter.ts @@ -1,33 +1,37 @@ import path from "node:path"; import { serve as honoServe } from "@hono/node-server"; import { serveStatic } from "@hono/node-server/serve-static"; -import { App, type CreateAppConfig, registries } from "bknd"; -import { registerLocalMediaAdapter } from "../index"; +import type { App } from "bknd"; +import { config as $config } from "core"; +import { type RuntimeBkndConfig, createRuntimeApp } from "../index"; -export type NodeAdapterOptions = CreateAppConfig & { - relativeDistPath?: string; +export type NodeBkndConfig = RuntimeBkndConfig & { port?: number; hostname?: string; listener?: Parameters[1]; - onBuilt?: (app: App) => Promise; - buildOptions?: Parameters[0]; + /** @deprecated */ + relativeDistPath?: string; }; export function serve({ + distPath, relativeDistPath, - port = 1337, + port = $config.server.default_port, hostname, listener, onBuilt, - buildOptions = {}, + buildConfig = {}, + beforeBuild, ...config -}: NodeAdapterOptions = {}) { - registerLocalMediaAdapter(); - +}: NodeBkndConfig = {}) { const root = path.relative( process.cwd(), - path.resolve(relativeDistPath ?? "./node_modules/bknd/dist", "static") + path.resolve(distPath ?? relativeDistPath ?? "./node_modules/bknd/dist", "static") ); + if (relativeDistPath) { + console.warn("relativeDistPath is deprecated, please use distPath instead"); + } + let app: App; honoServe( @@ -36,24 +40,11 @@ export function serve({ hostname, fetch: async (req: Request) => { if (!app) { - app = App.create(config); - - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async () => { - app.modules.server.get( - "/*", - serveStatic({ - root - }) - ); - app.registerAdminController(); - await onBuilt?.(app); - }, - "sync" - ); - - await app.build(buildOptions); + app = await createRuntimeApp({ + ...config, + registerLocalMedia: true, + serveStatic: serveStatic({ root }) + }); } return app.fetch(req); diff --git a/app/src/adapter/remix/remix.adapter.ts b/app/src/adapter/remix/remix.adapter.ts index 6c0c1f7..c3d0c78 100644 --- a/app/src/adapter/remix/remix.adapter.ts +++ b/app/src/adapter/remix/remix.adapter.ts @@ -1,12 +1,13 @@ -import { App, type CreateAppConfig } from "bknd"; +import { type FrameworkBkndConfig, createFrameworkApp } from "adapter"; +import type { App } from "bknd"; + +export type RemixBkndConfig = FrameworkBkndConfig; let app: App; -export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise }) { +export function serve(config: RemixBkndConfig = {}) { return async (args: { request: Request }) => { if (!app) { - app = App.create(config); - await config.beforeBuild?.(app); - await app.build(); + app = await createFrameworkApp(config); } return app.fetch(args.request); }; diff --git a/app/src/adapter/vite/vite.adapter.ts b/app/src/adapter/vite/vite.adapter.ts index 448f50d..686119f 100644 --- a/app/src/adapter/vite/vite.adapter.ts +++ b/app/src/adapter/vite/vite.adapter.ts @@ -1,47 +1,41 @@ import { serveStatic } from "@hono/node-server/serve-static"; -import type { BkndConfig } from "bknd"; -import { App } from "bknd"; +import { type RuntimeBkndConfig, createRuntimeApp } from "adapter"; +import type { CreateAppConfig } from "bknd"; +import type { App } from "bknd"; -function createApp(config: BkndConfig, env: any) { +export type ViteBkndConfig = RuntimeBkndConfig & { + app: CreateAppConfig | ((env: Env) => CreateAppConfig); + setAdminHtml?: boolean; + forceDev?: boolean; + html?: string; +}; + +async function createApp(config: ViteBkndConfig, env: any) { const create_config = typeof config.app === "function" ? config.app(env) : config.app; - return App.create(create_config); + return await createRuntimeApp({ + ...create_config, + adminOptions: config.setAdminHtml + ? { html: config.html, forceDev: config.forceDev } + : undefined, + serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })] + }); } -function setAppBuildListener(app: App, config: BkndConfig, html?: string) { - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async () => { - await config.onBuilt?.(app); - if (config.setAdminHtml) { - app.registerAdminController({ html, forceDev: true }); - app.module.server.client.get("/assets/*", serveStatic({ root: "./" })); - } - }, - "sync" - ); -} - -export async function serveFresh(config: BkndConfig, _html?: string) { +export async function serveFresh(config: ViteBkndConfig) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { - const app = createApp(config, env); - - setAppBuildListener(app, config, _html); - await app.build(); - + const app = await createApp(config, env); return app.fetch(request, env, ctx); } }; } let app: App; -export async function serveCached(config: BkndConfig, _html?: string) { +export async function serveCached(config: ViteBkndConfig) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { if (!app) { - app = createApp(config, env); - setAppBuildListener(app, config, _html); - await app.build(); + app = await createApp(config, env); } return app.fetch(request, env, ctx); diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index c123290..797d061 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -287,14 +287,18 @@ export class AppAuth extends Module { } catch (e) {} } - async createUser(input: { email: string; password: string }) { + async createUser({ + email, + password, + ...additional + }: { email: string; password: string; [key: string]: any }) { const strategy = "password"; const pw = this.authenticator.strategy(strategy) as PasswordStrategy; - const strategy_value = await pw.hash(input.password); + const strategy_value = await pw.hash(password); const mutator = this.em.mutator(this.config.entity_name as "users"); mutator.__unstable_toggleSystemEntityCreation(false); const { data: created } = await mutator.insertOne({ - email: input.email, + ...(additional as any), strategy, strategy_value }); diff --git a/app/src/cli/commands/run/run.ts b/app/src/cli/commands/run/run.ts index 2e1fb32..0b6c843 100644 --- a/app/src/cli/commands/run/run.ts +++ b/app/src/cli/commands/run/run.ts @@ -1,9 +1,9 @@ import type { Config } from "@libsql/client/node"; import { App, type CreateAppConfig } from "App"; -import type { BkndConfig } from "adapter"; import { StorageLocalAdapter } from "adapter/node"; -import type { CliCommand } from "cli/types"; +import type { CliBkndConfig, CliCommand } from "cli/types"; import { Option } from "commander"; +import { config } from "core"; import { registries } from "modules/registries"; import { PLATFORMS, @@ -21,7 +21,7 @@ export const run: CliCommand = (program) => { .addOption( new Option("-p, --port ", "port to run on") .env("PORT") - .default(1337) + .default(config.server.default_port) .argParser((v) => Number.parseInt(v)) ) .addOption(new Option("-c, --config ", "config file")) @@ -72,7 +72,7 @@ async function makeApp(config: MakeAppConfig) { return app; } -export async function makeConfigApp(config: BkndConfig, platform?: Platform) { +export async function makeConfigApp(config: CliBkndConfig, platform?: Platform) { const appConfig = typeof config.app === "function" ? config.app(process.env) : config.app; const app = App.create(appConfig); @@ -82,14 +82,13 @@ export async function makeConfigApp(config: BkndConfig, platform?: Platform) { await attachServeStatic(app, platform ?? "node"); app.registerAdminController(); - if (config.onBuilt) { - await config.onBuilt(app); - } + await config.onBuilt?.(app); }, "sync" ); - await app.build(); + await config.beforeBuild?.(app); + await app.build(config.buildConfig); return app; } @@ -110,7 +109,7 @@ async function action(options: { app = await makeApp({ connection, server: { platform: options.server } }); } else { console.log("Using config from:", configFilePath); - const config = (await import(configFilePath).then((m) => m.default)) as BkndConfig; + const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig; app = await makeConfigApp(config, options.server); } diff --git a/app/src/cli/commands/user.ts b/app/src/cli/commands/user.ts index 3a04d06..0883c67 100644 --- a/app/src/cli/commands/user.ts +++ b/app/src/cli/commands/user.ts @@ -1,10 +1,9 @@ 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 { makeConfigApp } from "cli/commands/run"; import { getConfigPath } from "cli/commands/run/platform"; -import type { CliCommand } from "cli/types"; +import type { CliBkndConfig, CliCommand } from "cli/types"; import { Argument } from "commander"; export const user: CliCommand = (program) => { @@ -22,7 +21,7 @@ async function action(action: "create" | "update", options: any) { return; } - const config = (await import(configFilePath).then((m) => m.default)) as BkndConfig; + const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig; const app = await makeConfigApp(config, options.server); switch (action) { diff --git a/app/src/cli/types.d.ts b/app/src/cli/types.d.ts index 6dd97aa..30bde3a 100644 --- a/app/src/cli/types.d.ts +++ b/app/src/cli/types.d.ts @@ -1,3 +1,14 @@ +import type { CreateAppConfig } from "App"; +import type { FrameworkBkndConfig } from "adapter"; import type { Command } from "commander"; export type CliCommand = (program: Command) => void; + +export type CliBkndConfig = FrameworkBkndConfig & { + app: CreateAppConfig | ((env: Env) => CreateAppConfig); + setAdminHtml?: boolean; + server?: { + port?: number; + platform?: "node" | "bun"; + }; +}; diff --git a/app/src/core/config.ts b/app/src/core/config.ts index fadff04..9a70a5c 100644 --- a/app/src/core/config.ts +++ b/app/src/core/config.ts @@ -9,6 +9,9 @@ export type PrimaryFieldType = number | Generated; export interface DB {} export const config = { + server: { + default_port: 1337 + }, data: { default_primary_field: "id" } diff --git a/examples/astro/src/pages/api/[...api].ts b/examples/astro/src/pages/api/[...api].ts index 37fe73c..abbcca7 100644 --- a/examples/astro/src/pages/api/[...api].ts +++ b/examples/astro/src/pages/api/[...api].ts @@ -1,4 +1,4 @@ -import { Api, App } from "bknd"; +import { App } from "bknd"; import { serve } from "bknd/adapter/astro"; import { registerLocalMediaAdapter } from "bknd/adapter/node"; import { boolean, em, entity, text } from "bknd/data"; diff --git a/examples/cloudflare-worker/src/index.ts b/examples/cloudflare-worker/src/index.ts index 6a79894..e7a0f5e 100644 --- a/examples/cloudflare-worker/src/index.ts +++ b/examples/cloudflare-worker/src/index.ts @@ -2,19 +2,17 @@ import { serve } from "bknd/adapter/cloudflare"; import manifest from "__STATIC_CONTENT_MANIFEST"; -export default serve( - { - app: (env: Env) => ({ - connection: { - type: "libsql", - config: { - url: "http://localhost:8080" - } +export default serve({ + app: (env: Env) => ({ + connection: { + type: "libsql", + config: { + url: "http://localhost:8080" } - }), - onBuilt: async (app) => { - app.modules.server.get("/hello", (c) => c.json({ hello: "world" })); } + }), + onBuilt: async (app) => { + app.modules.server.get("/custom", (c) => c.json({ hello: "world" })); }, manifest -); +}); diff --git a/examples/node/index.js b/examples/node/index.js index faa1fef..818fafd 100644 --- a/examples/node/index.js +++ b/examples/node/index.js @@ -4,7 +4,7 @@ import { serve } from "bknd/adapter/node"; // serve(); // this is optional, if omitted, it uses an in-memory database -/** @type {import("bknd/adapter/node").NodeAdapterOptions} */ +/** @type {import("bknd/adapter/node").NodeBkndConfig} */ const config = { connection: { type: "libsql", @@ -14,7 +14,7 @@ const config = { }, // this is only required to run inside the same workspace // leave blank if you're running this from a different project - relativeDistPath: "../../app/dist" + distPath: "../../app/dist" }; serve(config);