mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 12:37:20 +00:00
unified runtime and framework adapters
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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<void> }) {
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -1,64 +1,47 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
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<CreateAppConfig> & {
|
||||
distPath?: string;
|
||||
onBuilt?: (app: App) => Promise<void>;
|
||||
buildOptions?: Parameters<App["build"]>[0];
|
||||
};
|
||||
|
||||
export type BunBkndConfig = RuntimeBkndConfig & Omit<ServeOptions, "fetch">;
|
||||
|
||||
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<ServeOptions, "fetch"> & 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);
|
||||
|
||||
@@ -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<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 = {
|
||||
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":
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<Env = any> = {
|
||||
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<void>;
|
||||
beforeBuild?: (app: App) => Promise<void>;
|
||||
buildConfig?: Parameters<App["build"]>[0];
|
||||
};
|
||||
|
||||
// @todo: move to App
|
||||
export type BkndConfig<Env = any> = {
|
||||
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
||||
setAdminHtml?: boolean;
|
||||
server?: {
|
||||
port?: number;
|
||||
platform?: "node" | "bun";
|
||||
};
|
||||
cloudflare?: CloudflareBkndConfig<Env>;
|
||||
onBuilt?: (app: App) => Promise<void>;
|
||||
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<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 { 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<void> }) {
|
||||
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);
|
||||
|
||||
@@ -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<typeof honoServe>[1];
|
||||
onBuilt?: (app: App) => Promise<void>;
|
||||
buildOptions?: Parameters<App["build"]>[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);
|
||||
|
||||
@@ -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<void> }) {
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -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<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;
|
||||
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);
|
||||
|
||||
@@ -287,14 +287,18 @@ export class AppAuth extends Module<typeof authConfigSchema> {
|
||||
} 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
|
||||
});
|
||||
|
||||
@@ -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>", "port to run on")
|
||||
.env("PORT")
|
||||
.default(1337)
|
||||
.default(config.server.default_port)
|
||||
.argParser((v) => Number.parseInt(v))
|
||||
)
|
||||
.addOption(new Option("-c, --config <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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
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";
|
||||
|
||||
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 const config = {
|
||||
server: {
|
||||
default_port: 1337
|
||||
},
|
||||
data: {
|
||||
default_primary_field: "id"
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user