added fallback route to server, created extensive setup instructions in docs

This commit is contained in:
dswbx
2024-12-24 16:01:42 +01:00
parent 8ef11aa382
commit 06125f1afe
20 changed files with 564 additions and 174 deletions

View File

@@ -7,7 +7,6 @@ export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Cont
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
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;
@@ -15,28 +14,31 @@ export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Cont
ctx.waitUntil(kv!.put(key, JSON.stringify(__config)));
}
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);
const app = await createRuntimeApp(
{
...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);
},
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 }
},
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 }
});
env
);
if (!cachedConfig) {
saveConfig(app.toJSON(true));

View File

@@ -3,12 +3,13 @@ import type { App } from "bknd";
import type { CloudflareBkndConfig, Context } from "../index";
export async function makeApp(config: CloudflareBkndConfig, { env }: Context) {
const create_config = typeof config.app === "function" ? config.app(env) : config.app;
return await createRuntimeApp({
...config,
...create_config,
adminOptions: config.html ? { html: config.html } : undefined
});
return await createRuntimeApp(
{
...config,
adminOptions: config.html ? { html: config.html } : undefined
},
env
);
}
export async function getFresh(config: CloudflareBkndConfig, ctx: Context) {

View File

@@ -4,15 +4,16 @@ import type { MiddlewareHandler } from "hono";
import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
import type { AdminControllerOptions } from "modules/server/AdminController";
type BaseExternalBkndConfig = CreateAppConfig & {
export type BkndConfig<Env = any> = CreateAppConfig & {
app?: CreateAppConfig | ((env: Env) => CreateAppConfig);
onBuilt?: (app: App) => Promise<void>;
beforeBuild?: (app: App) => Promise<void>;
buildConfig?: Parameters<App["build"]>[0];
};
export type FrameworkBkndConfig = BaseExternalBkndConfig;
export type FrameworkBkndConfig<Env = any> = BkndConfig<Env>;
export type RuntimeBkndConfig = BaseExternalBkndConfig & {
export type RuntimeBkndConfig<Env = any> = BkndConfig<Env> & {
distPath?: string;
};
@@ -44,8 +45,27 @@ export function registerLocalMediaAdapter() {
registries.media.register("local", StorageLocalAdapter);
}
export async function createFrameworkApp(config: FrameworkBkndConfig): Promise<App> {
const app = App.create(config);
export function makeConfig<Env = any>(config: BkndConfig<Env>, env?: Env): CreateAppConfig {
let additionalConfig: CreateAppConfig = {};
if ("app" in config && config.app) {
if (typeof config.app === "function") {
if (!env) {
throw new Error("env is required when config.app is a function");
}
additionalConfig = config.app(env);
} else {
additionalConfig = config.app;
}
}
return { ...config, ...additionalConfig };
}
export async function createFrameworkApp<Env = any>(
config: FrameworkBkndConfig,
env?: Env
): Promise<App> {
const app = App.create(makeConfig(config, env));
if (config.onBuilt) {
app.emgr.onEvent(
@@ -63,21 +83,24 @@ export async function createFrameworkApp(config: FrameworkBkndConfig): Promise<A
return app;
}
export async function createRuntimeApp({
serveStatic,
registerLocalMedia,
adminOptions,
...config
}: RuntimeBkndConfig & {
serveStatic?: MiddlewareHandler | [string, MiddlewareHandler];
registerLocalMedia?: boolean;
adminOptions?: AdminControllerOptions | false;
}): Promise<App> {
export async function createRuntimeApp<Env = any>(
{
serveStatic,
registerLocalMedia,
adminOptions,
...config
}: RuntimeBkndConfig & {
serveStatic?: MiddlewareHandler | [string, MiddlewareHandler];
registerLocalMedia?: boolean;
adminOptions?: AdminControllerOptions | false;
},
env?: Env
): Promise<App> {
if (registerLocalMedia) {
registerLocalMediaAdapter();
}
const app = App.create(config);
const app = App.create(makeConfig(config, env));
app.emgr.onEvent(
App.Events.AppBuiltEvent,

View File

@@ -1,24 +1,40 @@
import { serveStatic } from "@hono/node-server/serve-static";
import { type RuntimeBkndConfig, createRuntimeApp } from "adapter";
import type { CreateAppConfig } from "bknd";
import type { App } from "bknd";
export type ViteBkndConfig<Env = any> = RuntimeBkndConfig & {
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
export type ViteBkndConfig<Env = any> = RuntimeBkndConfig<Env> & {
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 await createRuntimeApp({
...create_config,
adminOptions: config.setAdminHtml
? { html: config.html, forceDev: config.forceDev }
: undefined,
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })]
});
export function addViteScript(html: string, addBkndContext: boolean = true) {
return html.replace(
"</head>",
`<script type="module">
import RefreshRuntime from "/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>
<script type="module" src="/@vite/client"></script>
${addBkndContext ? "<!-- BKND_CONTEXT -->" : ""}
</head>`
);
}
async function createApp(config: ViteBkndConfig, env?: any) {
return await createRuntimeApp(
{
...config,
adminOptions: config.setAdminHtml
? { html: config.html, forceDev: config.forceDev }
: undefined,
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })]
},
env
);
}
export async function serveFresh(config: ViteBkndConfig) {