mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Merge pull request #34 from bknd-io/refactor/adapter-options
Refactor: Unified framework and runtime adapters
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.4.0-rc1",
|
"version": "0.4.0-rc2",
|
||||||
"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",
|
||||||
|
|||||||
@@ -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 = {
|
type TAstro = {
|
||||||
request: Request;
|
request: Request;
|
||||||
@@ -18,12 +21,10 @@ export function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise<void> }) {
|
export function serve(config: AstroBkndConfig = {}) {
|
||||||
return async (args: TAstro) => {
|
return async (args: TAstro) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = App.create(config);
|
app = await createFrameworkApp(config);
|
||||||
await config.beforeBuild?.(app);
|
|
||||||
await app.build();
|
|
||||||
}
|
}
|
||||||
return app.fetch(args.request);
|
return app.fetch(args.request);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,64 +1,47 @@
|
|||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { App, type CreateAppConfig, registries } from "bknd";
|
import type { App } from "bknd";
|
||||||
import type { Serve, ServeOptions } from "bun";
|
import type { ServeOptions } from "bun";
|
||||||
|
import { config } from "core";
|
||||||
import { serveStatic } from "hono/bun";
|
import { serveStatic } from "hono/bun";
|
||||||
import { registerLocalMediaAdapter } from "../index";
|
import { type RuntimeBkndConfig, createRuntimeApp } from "../index";
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export type ExtendedAppCreateConfig = Partial<CreateAppConfig> & {
|
|
||||||
distPath?: string;
|
export type BunBkndConfig = RuntimeBkndConfig & Omit<ServeOptions, "fetch">;
|
||||||
onBuilt?: (app: App) => Promise<void>;
|
|
||||||
buildOptions?: Parameters<App["build"]>[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function createApp({
|
export async function createApp({
|
||||||
distPath,
|
distPath,
|
||||||
onBuilt,
|
onBuilt,
|
||||||
buildOptions,
|
buildConfig,
|
||||||
|
beforeBuild,
|
||||||
...config
|
...config
|
||||||
}: ExtendedAppCreateConfig) {
|
}: RuntimeBkndConfig = {}) {
|
||||||
registerLocalMediaAdapter();
|
|
||||||
const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static");
|
const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static");
|
||||||
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = App.create(config);
|
app = await createRuntimeApp({
|
||||||
|
...config,
|
||||||
app.emgr.onEvent(
|
registerLocalMedia: true,
|
||||||
App.Events.AppBuiltEvent,
|
serveStatic: serveStatic({ root })
|
||||||
async () => {
|
});
|
||||||
app.modules.server.get(
|
|
||||||
"/*",
|
|
||||||
serveStatic({
|
|
||||||
root
|
|
||||||
})
|
|
||||||
);
|
|
||||||
app.registerAdminController();
|
|
||||||
await onBuilt?.(app);
|
|
||||||
},
|
|
||||||
"sync"
|
|
||||||
);
|
|
||||||
|
|
||||||
await app.build(buildOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BunAdapterOptions = Omit<ServeOptions, "fetch"> & ExtendedAppCreateConfig;
|
|
||||||
|
|
||||||
export function serve({
|
export function serve({
|
||||||
distPath,
|
distPath,
|
||||||
connection,
|
connection,
|
||||||
initialConfig,
|
initialConfig,
|
||||||
plugins,
|
plugins,
|
||||||
options,
|
options,
|
||||||
port = 1337,
|
port = config.server.default_port,
|
||||||
onBuilt,
|
onBuilt,
|
||||||
buildOptions,
|
buildConfig,
|
||||||
...serveOptions
|
...serveOptions
|
||||||
}: BunAdapterOptions = {}) {
|
}: BunBkndConfig = {}) {
|
||||||
Bun.serve({
|
Bun.serve({
|
||||||
...serveOptions,
|
...serveOptions,
|
||||||
port,
|
port,
|
||||||
@@ -69,7 +52,7 @@ export function serve({
|
|||||||
plugins,
|
plugins,
|
||||||
options,
|
options,
|
||||||
onBuilt,
|
onBuilt,
|
||||||
buildOptions,
|
buildConfig,
|
||||||
distPath
|
distPath
|
||||||
});
|
});
|
||||||
return app.fetch(request);
|
return app.fetch(request);
|
||||||
|
|||||||
@@ -1,22 +1,37 @@
|
|||||||
|
import type { CreateAppConfig } from "bknd";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { serveStatic } from "hono/cloudflare-workers";
|
import { serveStatic } from "hono/cloudflare-workers";
|
||||||
import type { BkndConfig } from "../index";
|
import type { FrameworkBkndConfig } from "../index";
|
||||||
import { getCached } from "./modes/cached";
|
import { getCached } from "./modes/cached";
|
||||||
import { getDurable } from "./modes/durable";
|
import { getDurable } from "./modes/durable";
|
||||||
import { getFresh, getWarm } from "./modes/fresh";
|
import { getFresh, getWarm } from "./modes/fresh";
|
||||||
|
|
||||||
|
export type CloudflareBkndConfig<Env = any> = Omit<FrameworkBkndConfig, "app"> & {
|
||||||
|
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 = {
|
export type Context = {
|
||||||
request: Request;
|
request: Request;
|
||||||
env: any;
|
env: any;
|
||||||
ctx: ExecutionContext;
|
ctx: ExecutionContext;
|
||||||
manifest: any;
|
|
||||||
html?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function serve(_config: BkndConfig, manifest?: string, html?: string) {
|
export function serve(config: CloudflareBkndConfig) {
|
||||||
return {
|
return {
|
||||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
const manifest = config.manifest;
|
||||||
|
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
const pathname = url.pathname.slice(1);
|
const pathname = url.pathname.slice(1);
|
||||||
@@ -27,8 +42,7 @@ export function serve(_config: BkndConfig, manifest?: string, html?: string) {
|
|||||||
hono.all("*", async (c, next) => {
|
hono.all("*", async (c, next) => {
|
||||||
const res = await serveStatic({
|
const res = await serveStatic({
|
||||||
path: `./${pathname}`,
|
path: `./${pathname}`,
|
||||||
manifest,
|
manifest
|
||||||
onNotFound: (path) => console.log("not found", path)
|
|
||||||
})(c as any, next);
|
})(c as any, next);
|
||||||
if (res instanceof Response) {
|
if (res instanceof Response) {
|
||||||
const ttl = 60 * 60 * 24 * 365;
|
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 && !!config.manifest;
|
||||||
..._config,
|
|
||||||
setAdminHtml: _config.setAdminHtml ?? !!manifest
|
const context = { request, env, ctx } as Context;
|
||||||
};
|
const mode = config.mode ?? "warm";
|
||||||
const context = { request, env, ctx, manifest, html } as Context;
|
|
||||||
const mode = config.cloudflare?.mode ?? "warm";
|
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "fresh":
|
case "fresh":
|
||||||
|
|||||||
@@ -1,38 +1,31 @@
|
|||||||
import type { BkndConfig } from "adapter";
|
import { createRuntimeApp } from "adapter";
|
||||||
import { App } from "bknd";
|
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) {
|
export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Context) {
|
||||||
const { kv } = config.cloudflare?.bindings?.(env)!;
|
const { kv } = config.bindings?.(env)!;
|
||||||
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
|
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 create_config = typeof config.app === "function" ? config.app(env) : config.app;
|
||||||
const cachedConfig = await kv.get(key);
|
const cachedConfig = await kv.get(key);
|
||||||
const initialConfig = cachedConfig ? JSON.parse(cachedConfig) : undefined;
|
const initialConfig = cachedConfig ? JSON.parse(cachedConfig) : undefined;
|
||||||
|
|
||||||
const app = App.create({ ...create_config, initialConfig });
|
|
||||||
|
|
||||||
async function saveConfig(__config: any) {
|
async function saveConfig(__config: any) {
|
||||||
ctx.waitUntil(kv!.put(key, JSON.stringify(__config)));
|
ctx.waitUntil(kv!.put(key, JSON.stringify(__config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.onBuilt) {
|
const app = await createRuntimeApp({
|
||||||
app.emgr.onEvent(
|
...create_config,
|
||||||
App.Events.AppBuiltEvent,
|
initialConfig,
|
||||||
async ({ params: { app } }) => {
|
onBuilt: async (app) => {
|
||||||
app.module.server.client.get("/__bknd/cache", async (c) => {
|
app.module.server.client.get("/__bknd/cache", async (c) => {
|
||||||
await kv.delete(key);
|
await kv.delete(key);
|
||||||
return c.json({ message: "Cache cleared" });
|
return c.json({ message: "Cache cleared" });
|
||||||
});
|
});
|
||||||
app.registerAdminController({ html });
|
await config.onBuilt?.(app);
|
||||||
|
|
||||||
config.onBuilt!(app);
|
|
||||||
},
|
},
|
||||||
"sync"
|
beforeBuild: async (app) => {
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.emgr.onEvent(
|
app.emgr.onEvent(
|
||||||
App.Events.AppConfigUpdatedEvent,
|
App.Events.AppConfigUpdatedEvent,
|
||||||
async ({ params: { app } }) => {
|
async ({ params: { app } }) => {
|
||||||
@@ -40,12 +33,10 @@ export async function getCached(config: BkndConfig, { env, html, ctx }: Context)
|
|||||||
},
|
},
|
||||||
"sync"
|
"sync"
|
||||||
);
|
);
|
||||||
|
await config.beforeBuild?.(app);
|
||||||
await app.build();
|
},
|
||||||
|
adminOptions: { html: config.html }
|
||||||
if (config.setAdminHtml) {
|
});
|
||||||
app.registerAdminController({ html });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cachedConfig) {
|
if (!cachedConfig) {
|
||||||
saveConfig(app.toJSON(true));
|
saveConfig(app.toJSON(true));
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { DurableObject } from "cloudflare:workers";
|
import { DurableObject } from "cloudflare:workers";
|
||||||
import type { BkndConfig } from "adapter";
|
import { createRuntimeApp } from "adapter";
|
||||||
import type { Context } from "adapter/cloudflare";
|
import type { CloudflareBkndConfig, Context } from "adapter/cloudflare";
|
||||||
import { App, type CreateAppConfig } from "bknd";
|
import type { App, CreateAppConfig } from "bknd";
|
||||||
|
|
||||||
export async function getDurable(config: BkndConfig, ctx: Context) {
|
export async function getDurable(config: CloudflareBkndConfig, ctx: Context) {
|
||||||
const { dobj } = config.cloudflare?.bindings?.(ctx.env)!;
|
const { dobj } = config.bindings?.(ctx.env)!;
|
||||||
if (!dobj) throw new Error("durable object is not defined in cloudflare.bindings");
|
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) {
|
if ([config.onBuilt, config.beforeBuild].some((x) => x)) {
|
||||||
console.log("onBuilt() is not supported with DurableObject mode");
|
console.log("onBuilt and beforeBuild are not supported with DurableObject mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
@@ -21,8 +21,8 @@ export async function getDurable(config: BkndConfig, ctx: Context) {
|
|||||||
|
|
||||||
const res = await stub.fire(ctx.request, {
|
const res = await stub.fire(ctx.request, {
|
||||||
config: create_config,
|
config: create_config,
|
||||||
html: ctx.html,
|
html: config.html,
|
||||||
keepAliveSeconds: config.cloudflare?.keepAliveSeconds,
|
keepAliveSeconds: config.keepAliveSeconds,
|
||||||
setAdminHtml: config.setAdminHtml
|
setAdminHtml: config.setAdminHtml
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,10 +64,9 @@ export class DurableBkndApp extends DurableObject {
|
|||||||
config.connection.config.protocol = "wss";
|
config.connection.config.protocol = "wss";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.app = App.create(config);
|
this.app = await createRuntimeApp({
|
||||||
this.app.emgr.onEvent(
|
...config,
|
||||||
App.Events.AppBuiltEvent,
|
onBuilt: async (app) => {
|
||||||
async ({ params: { app } }) => {
|
|
||||||
app.modules.server.get("/__do", async (c) => {
|
app.modules.server.get("/__do", async (c) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const context: any = c.req.raw.cf ? c.req.raw.cf : c.env.cf;
|
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);
|
await this.onBuilt(app);
|
||||||
},
|
},
|
||||||
"sync"
|
adminOptions: { html: options.html },
|
||||||
);
|
beforeBuild: async (app) => {
|
||||||
|
await this.beforeBuild(app);
|
||||||
await this.app.build();
|
|
||||||
|
|
||||||
if (options.setAdminHtml) {
|
|
||||||
this.app.registerAdminController({ html: options.html });
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
buildtime = performance.now() - start;
|
buildtime = performance.now() - start;
|
||||||
}
|
}
|
||||||
@@ -110,6 +106,7 @@ export class DurableBkndApp extends DurableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onBuilt(app: App) {}
|
async onBuilt(app: App) {}
|
||||||
|
async beforeBuild(app: App) {}
|
||||||
|
|
||||||
protected keepAlive(seconds: number) {
|
protected keepAlive(seconds: number) {
|
||||||
console.log("keep alive for", seconds);
|
console.log("keep alive for", seconds);
|
||||||
|
|||||||
@@ -1,36 +1,23 @@
|
|||||||
import type { BkndConfig } from "adapter";
|
import { createRuntimeApp } from "adapter";
|
||||||
import { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
import type { Context } from "../index";
|
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 create_config = typeof config.app === "function" ? config.app(env) : config.app;
|
||||||
const app = App.create(create_config);
|
return await createRuntimeApp({
|
||||||
|
...config,
|
||||||
if (config.onBuilt) {
|
...create_config,
|
||||||
app.emgr.onEvent(
|
adminOptions: config.html ? { html: config.html } : undefined
|
||||||
App.Events.AppBuiltEvent,
|
});
|
||||||
async ({ params: { app } }) => {
|
|
||||||
config.onBuilt!(app);
|
|
||||||
},
|
|
||||||
"sync"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
if (config.setAdminHtml) {
|
|
||||||
app.registerAdminController({ html });
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFresh(config: BkndConfig, ctx: Context) {
|
export async function getFresh(config: CloudflareBkndConfig, ctx: Context) {
|
||||||
const app = await makeApp(config, ctx);
|
const app = await makeApp(config, ctx);
|
||||||
return app.fetch(ctx.request);
|
return app.fetch(ctx.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
let warm_app: App;
|
let warm_app: App;
|
||||||
export async function getWarm(config: BkndConfig, ctx: Context) {
|
export async function getWarm(config: CloudflareBkndConfig, ctx: Context) {
|
||||||
if (!warm_app) {
|
if (!warm_app) {
|
||||||
warm_app = await makeApp(config, ctx);
|
warm_app = await makeApp(config, ctx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,19 @@
|
|||||||
import type { IncomingMessage } from "node:http";
|
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 { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
|
||||||
|
import type { AdminControllerOptions } from "modules/server/AdminController";
|
||||||
|
|
||||||
export type CloudflareBkndConfig<Env = any> = {
|
type BaseExternalBkndConfig = CreateAppConfig & {
|
||||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
onBuilt?: (app: App) => Promise<void>;
|
||||||
bindings?: (env: Env) => {
|
beforeBuild?: (app: App) => Promise<void>;
|
||||||
kv?: KVNamespace;
|
buildConfig?: Parameters<App["build"]>[0];
|
||||||
dobj?: DurableObjectNamespace;
|
|
||||||
};
|
|
||||||
key?: string;
|
|
||||||
keepAliveSeconds?: number;
|
|
||||||
forceHttps?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// @todo: move to App
|
export type FrameworkBkndConfig = BaseExternalBkndConfig;
|
||||||
export type BkndConfig<Env = any> = {
|
|
||||||
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
export type RuntimeBkndConfig = BaseExternalBkndConfig & {
|
||||||
setAdminHtml?: boolean;
|
distPath?: string;
|
||||||
server?: {
|
|
||||||
port?: number;
|
|
||||||
platform?: "node" | "bun";
|
|
||||||
};
|
|
||||||
cloudflare?: CloudflareBkndConfig<Env>;
|
|
||||||
onBuilt?: (app: App) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function nodeRequestToRequest(req: IncomingMessage): Request {
|
export function nodeRequestToRequest(req: IncomingMessage): Request {
|
||||||
@@ -52,3 +43,64 @@ export function nodeRequestToRequest(req: IncomingMessage): Request {
|
|||||||
export function registerLocalMediaAdapter() {
|
export function registerLocalMediaAdapter() {
|
||||||
registries.media.register("local", StorageLocalAdapter);
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createFrameworkApp(config: FrameworkBkndConfig): Promise<App> {
|
||||||
|
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<App> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||||
import { Api, App, type CreateAppConfig } from "bknd";
|
import { Api, type App } from "bknd";
|
||||||
import { nodeRequestToRequest } from "../index";
|
import { type FrameworkBkndConfig, createFrameworkApp, nodeRequestToRequest } from "../index";
|
||||||
|
|
||||||
|
export type NextjsBkndConfig = FrameworkBkndConfig;
|
||||||
|
|
||||||
type GetServerSidePropsContext = {
|
type GetServerSidePropsContext = {
|
||||||
req: IncomingMessage;
|
req: IncomingMessage;
|
||||||
@@ -42,12 +44,10 @@ function getCleanRequest(req: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise<void> }) {
|
export function serve(config: NextjsBkndConfig = {}) {
|
||||||
return async (req: Request) => {
|
return async (req: Request) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = App.create(config);
|
app = await createFrameworkApp(config);
|
||||||
await config.beforeBuild?.(app);
|
|
||||||
await app.build();
|
|
||||||
}
|
}
|
||||||
const request = getCleanRequest(req);
|
const request = getCleanRequest(req);
|
||||||
return app.fetch(request, process.env);
|
return app.fetch(request, process.env);
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { serve as honoServe } from "@hono/node-server";
|
import { serve as honoServe } from "@hono/node-server";
|
||||||
import { serveStatic } from "@hono/node-server/serve-static";
|
import { serveStatic } from "@hono/node-server/serve-static";
|
||||||
import { App, type CreateAppConfig, registries } from "bknd";
|
import type { App } from "bknd";
|
||||||
import { registerLocalMediaAdapter } from "../index";
|
import { config as $config } from "core";
|
||||||
|
import { type RuntimeBkndConfig, createRuntimeApp } from "../index";
|
||||||
|
|
||||||
export type NodeAdapterOptions = CreateAppConfig & {
|
export type NodeBkndConfig = RuntimeBkndConfig & {
|
||||||
relativeDistPath?: string;
|
|
||||||
port?: number;
|
port?: number;
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
listener?: Parameters<typeof honoServe>[1];
|
listener?: Parameters<typeof honoServe>[1];
|
||||||
onBuilt?: (app: App) => Promise<void>;
|
/** @deprecated */
|
||||||
buildOptions?: Parameters<App["build"]>[0];
|
relativeDistPath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function serve({
|
export function serve({
|
||||||
|
distPath,
|
||||||
relativeDistPath,
|
relativeDistPath,
|
||||||
port = 1337,
|
port = $config.server.default_port,
|
||||||
hostname,
|
hostname,
|
||||||
listener,
|
listener,
|
||||||
onBuilt,
|
onBuilt,
|
||||||
buildOptions = {},
|
buildConfig = {},
|
||||||
|
beforeBuild,
|
||||||
...config
|
...config
|
||||||
}: NodeAdapterOptions = {}) {
|
}: NodeBkndConfig = {}) {
|
||||||
registerLocalMediaAdapter();
|
|
||||||
|
|
||||||
const root = path.relative(
|
const root = path.relative(
|
||||||
process.cwd(),
|
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;
|
let app: App;
|
||||||
|
|
||||||
honoServe(
|
honoServe(
|
||||||
@@ -36,24 +40,11 @@ export function serve({
|
|||||||
hostname,
|
hostname,
|
||||||
fetch: async (req: Request) => {
|
fetch: async (req: Request) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = App.create(config);
|
app = await createRuntimeApp({
|
||||||
|
...config,
|
||||||
app.emgr.onEvent(
|
registerLocalMedia: true,
|
||||||
App.Events.AppBuiltEvent,
|
serveStatic: serveStatic({ root })
|
||||||
async () => {
|
});
|
||||||
app.modules.server.get(
|
|
||||||
"/*",
|
|
||||||
serveStatic({
|
|
||||||
root
|
|
||||||
})
|
|
||||||
);
|
|
||||||
app.registerAdminController();
|
|
||||||
await onBuilt?.(app);
|
|
||||||
},
|
|
||||||
"sync"
|
|
||||||
);
|
|
||||||
|
|
||||||
await app.build(buildOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.fetch(req);
|
return app.fetch(req);
|
||||||
|
|||||||
@@ -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;
|
let app: App;
|
||||||
export function serve(config: CreateAppConfig & { beforeBuild?: (app: App) => Promise<void> }) {
|
export function serve(config: RemixBkndConfig = {}) {
|
||||||
return async (args: { request: Request }) => {
|
return async (args: { request: Request }) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = App.create(config);
|
app = await createFrameworkApp(config);
|
||||||
await config.beforeBuild?.(app);
|
|
||||||
await app.build();
|
|
||||||
}
|
}
|
||||||
return app.fetch(args.request);
|
return app.fetch(args.request);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,47 +1,41 @@
|
|||||||
import { serveStatic } from "@hono/node-server/serve-static";
|
import { serveStatic } from "@hono/node-server/serve-static";
|
||||||
import type { BkndConfig } from "bknd";
|
import { type RuntimeBkndConfig, createRuntimeApp } from "adapter";
|
||||||
import { App } from "bknd";
|
import type { CreateAppConfig } from "bknd";
|
||||||
|
import type { App } from "bknd";
|
||||||
|
|
||||||
function createApp(config: BkndConfig, env: any) {
|
export type ViteBkndConfig<Env = any> = 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;
|
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) {
|
export async function serveFresh(config: ViteBkndConfig) {
|
||||||
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) {
|
|
||||||
return {
|
return {
|
||||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||||
const app = createApp(config, env);
|
const app = await createApp(config, env);
|
||||||
|
|
||||||
setAppBuildListener(app, config, _html);
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
return app.fetch(request, env, ctx);
|
return app.fetch(request, env, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export async function serveCached(config: BkndConfig, _html?: string) {
|
export async function serveCached(config: ViteBkndConfig) {
|
||||||
return {
|
return {
|
||||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = createApp(config, env);
|
app = await createApp(config, env);
|
||||||
setAppBuildListener(app, config, _html);
|
|
||||||
await app.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.fetch(request, env, ctx);
|
return app.fetch(request, env, ctx);
|
||||||
|
|||||||
@@ -287,14 +287,18 @@ export class AppAuth extends Module<typeof authConfigSchema> {
|
|||||||
} catch (e) {}
|
} 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 strategy = "password";
|
||||||
const pw = this.authenticator.strategy(strategy) as PasswordStrategy;
|
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");
|
const mutator = this.em.mutator(this.config.entity_name as "users");
|
||||||
mutator.__unstable_toggleSystemEntityCreation(false);
|
mutator.__unstable_toggleSystemEntityCreation(false);
|
||||||
const { data: created } = await mutator.insertOne({
|
const { data: created } = await mutator.insertOne({
|
||||||
email: input.email,
|
...(additional as any),
|
||||||
strategy,
|
strategy,
|
||||||
strategy_value
|
strategy_value
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { Config } from "@libsql/client/node";
|
import type { Config } from "@libsql/client/node";
|
||||||
import { App, type CreateAppConfig } from "App";
|
import { App, type CreateAppConfig } from "App";
|
||||||
import type { BkndConfig } from "adapter";
|
|
||||||
import { StorageLocalAdapter } from "adapter/node";
|
import { StorageLocalAdapter } from "adapter/node";
|
||||||
import type { CliCommand } from "cli/types";
|
import type { CliBkndConfig, CliCommand } from "cli/types";
|
||||||
import { Option } from "commander";
|
import { Option } from "commander";
|
||||||
|
import { config } from "core";
|
||||||
import { registries } from "modules/registries";
|
import { registries } from "modules/registries";
|
||||||
import {
|
import {
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
@@ -21,7 +21,7 @@ export const run: CliCommand = (program) => {
|
|||||||
.addOption(
|
.addOption(
|
||||||
new Option("-p, --port <port>", "port to run on")
|
new Option("-p, --port <port>", "port to run on")
|
||||||
.env("PORT")
|
.env("PORT")
|
||||||
.default(1337)
|
.default(config.server.default_port)
|
||||||
.argParser((v) => Number.parseInt(v))
|
.argParser((v) => Number.parseInt(v))
|
||||||
)
|
)
|
||||||
.addOption(new Option("-c, --config <config>", "config file"))
|
.addOption(new Option("-c, --config <config>", "config file"))
|
||||||
@@ -72,7 +72,7 @@ async function makeApp(config: MakeAppConfig) {
|
|||||||
return app;
|
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 appConfig = typeof config.app === "function" ? config.app(process.env) : config.app;
|
||||||
const app = App.create(appConfig);
|
const app = App.create(appConfig);
|
||||||
|
|
||||||
@@ -82,14 +82,13 @@ export async function makeConfigApp(config: BkndConfig, platform?: Platform) {
|
|||||||
await attachServeStatic(app, platform ?? "node");
|
await attachServeStatic(app, platform ?? "node");
|
||||||
app.registerAdminController();
|
app.registerAdminController();
|
||||||
|
|
||||||
if (config.onBuilt) {
|
await config.onBuilt?.(app);
|
||||||
await config.onBuilt(app);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sync"
|
"sync"
|
||||||
);
|
);
|
||||||
|
|
||||||
await app.build();
|
await config.beforeBuild?.(app);
|
||||||
|
await app.build(config.buildConfig);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ async function action(options: {
|
|||||||
app = await makeApp({ connection, server: { platform: options.server } });
|
app = await makeApp({ connection, server: { platform: options.server } });
|
||||||
} else {
|
} else {
|
||||||
console.log("Using config from:", configFilePath);
|
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);
|
app = await makeConfigApp(config, options.server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
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 { App } from "App";
|
||||||
import type { BkndConfig } from "adapter";
|
|
||||||
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
||||||
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 { CliBkndConfig, CliCommand } from "cli/types";
|
||||||
import { Argument } from "commander";
|
import { Argument } from "commander";
|
||||||
|
|
||||||
export const user: CliCommand = (program) => {
|
export const user: CliCommand = (program) => {
|
||||||
@@ -22,7 +21,7 @@ async function action(action: "create" | "update", options: any) {
|
|||||||
return;
|
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);
|
const app = await makeConfigApp(config, options.server);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|||||||
11
app/src/cli/types.d.ts
vendored
11
app/src/cli/types.d.ts
vendored
@@ -1,3 +1,14 @@
|
|||||||
|
import type { CreateAppConfig } from "App";
|
||||||
|
import type { FrameworkBkndConfig } from "adapter";
|
||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
|
|
||||||
export type CliCommand = (program: Command) => void;
|
export type CliCommand = (program: Command) => void;
|
||||||
|
|
||||||
|
export type CliBkndConfig<Env = any> = FrameworkBkndConfig & {
|
||||||
|
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
||||||
|
setAdminHtml?: boolean;
|
||||||
|
server?: {
|
||||||
|
port?: number;
|
||||||
|
platform?: "node" | "bun";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ export type PrimaryFieldType = number | Generated<number>;
|
|||||||
export interface DB {}
|
export interface DB {}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
server: {
|
||||||
|
default_port: 1337
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
default_primary_field: "id"
|
default_primary_field: "id"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Api, App } from "bknd";
|
import { 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";
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { serve } from "bknd/adapter/cloudflare";
|
|||||||
|
|
||||||
import manifest from "__STATIC_CONTENT_MANIFEST";
|
import manifest from "__STATIC_CONTENT_MANIFEST";
|
||||||
|
|
||||||
export default serve(
|
export default serve({
|
||||||
{
|
|
||||||
app: (env: Env) => ({
|
app: (env: Env) => ({
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
@@ -13,8 +12,7 @@ export default serve(
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
onBuilt: async (app) => {
|
onBuilt: async (app) => {
|
||||||
app.modules.server.get("/hello", (c) => c.json({ hello: "world" }));
|
app.modules.server.get("/custom", (c) => c.json({ hello: "world" }));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
manifest
|
manifest
|
||||||
);
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { serve } from "bknd/adapter/node";
|
|||||||
// serve();
|
// serve();
|
||||||
|
|
||||||
// this is optional, if omitted, it uses an in-memory database
|
// 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 = {
|
const config = {
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
@@ -14,7 +14,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
// this is only required to run inside the same workspace
|
// this is only required to run inside the same workspace
|
||||||
// leave blank if you're running this from a different project
|
// leave blank if you're running this from a different project
|
||||||
relativeDistPath: "../../app/dist"
|
distPath: "../../app/dist"
|
||||||
};
|
};
|
||||||
|
|
||||||
serve(config);
|
serve(config);
|
||||||
|
|||||||
Reference in New Issue
Block a user