added a few initial plugins

This commit is contained in:
dswbx
2025-06-12 19:58:18 +02:00
parent fe5ccd4206
commit 8517c9b90b
12 changed files with 125 additions and 8 deletions

View File

@@ -51,6 +51,9 @@ describe("App tests", async () => {
}, },
); );
}, },
onBoot: async () => {
called.push("onBoot");
},
beforeBuild: async () => { beforeBuild: async () => {
called.push("beforeBuild"); called.push("beforeBuild");
}, },
@@ -90,6 +93,7 @@ describe("App tests", async () => {
}, },
]); ]);
expect(called).toEqual([ expect(called).toEqual([
"onBoot",
"onServerInit", "onServerInit",
"beforeBuild", "beforeBuild",
"onServerInit", "onServerInit",

View File

@@ -78,6 +78,7 @@ async function buildApi() {
"src/core/utils/index.ts", "src/core/utils/index.ts",
"src/data/index.ts", "src/data/index.ts",
"src/media/index.ts", "src/media/index.ts",
"src/plugins/index.ts",
], ],
outDir: "dist", outDir: "dist",
external: [...external], external: [...external],

View File

@@ -183,6 +183,11 @@
"import": "./dist/media/index.js", "import": "./dist/media/index.js",
"require": "./dist/media/index.js" "require": "./dist/media/index.js"
}, },
"./plugins": {
"types": "./dist/types/plugins/index.d.ts",
"import": "./dist/plugins/index.js",
"require": "./dist/plugins/index.js"
},
"./adapter/cloudflare": { "./adapter/cloudflare": {
"types": "./dist/types/adapter/cloudflare/index.d.ts", "types": "./dist/types/adapter/cloudflare/index.d.ts",
"import": "./dist/adapter/cloudflare/index.js", "import": "./dist/adapter/cloudflare/index.js",
@@ -231,6 +236,23 @@
"./dist/styles.css": "./dist/ui/styles.css", "./dist/styles.css": "./dist/ui/styles.css",
"./dist/manifest.json": "./dist/static/.vite/manifest.json" "./dist/manifest.json": "./dist/static/.vite/manifest.json"
}, },
"typesVersions": {
"*": {
"data": ["./dist/types/data/index.d.ts"],
"core": ["./dist/types/core/index.d.ts"],
"utils": ["./dist/types/core/utils/index.d.ts"],
"cli": ["./dist/types/cli/index.d.ts"],
"media": ["./dist/types/media/index.d.ts"],
"plugins": ["./dist/types/plugins/index.d.ts"],
"adapter": ["./dist/types/adapter/index.d.ts"],
"adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"],
"adapter/vite": ["./dist/types/adapter/vite/index.d.ts"],
"adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"],
"adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"],
"adapter/bun": ["./dist/types/adapter/bun/index.d.ts"],
"adapter/node": ["./dist/types/adapter/node/index.d.ts"]
}
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@@ -27,6 +27,7 @@ export type AppPluginConfig = {
onBuilt?: () => MaybePromise<void>; onBuilt?: () => MaybePromise<void>;
onServerInit?: (server: Hono<ServerEnv>) => MaybePromise<void>; onServerInit?: (server: Hono<ServerEnv>) => MaybePromise<void>;
onFirstBoot?: () => MaybePromise<void>; onFirstBoot?: () => MaybePromise<void>;
onBoot?: () => MaybePromise<void>;
}; };
export type AppPlugin = (app: App) => AppPluginConfig; export type AppPlugin = (app: App) => AppPluginConfig;
@@ -93,6 +94,7 @@ export class App {
private options?: AppOptions, private options?: AppOptions,
) { ) {
this.plugins = (options?.plugins ?? []).map((plugin) => plugin(this)); this.plugins = (options?.plugins ?? []).map((plugin) => plugin(this));
this.runPlugins("onBoot");
this.modules = new ModuleManager(connection, { this.modules = new ModuleManager(connection, {
...(options?.manager ?? {}), ...(options?.manager ?? {}),
initial: _initialConfig, initial: _initialConfig,

View File

@@ -1,5 +1,4 @@
import { D1Connection, type D1ConnectionConfig } from "./connection/D1Connection"; import { D1Connection, type D1ConnectionConfig } from "./connection/D1Connection";
import { ImageOptimizationPlugin } from "./plugins/image-optimization.plugin";
export * from "./cloudflare-workers.adapter"; export * from "./cloudflare-workers.adapter";
export { makeApp, getFresh } from "./modes/fresh"; export { makeApp, getFresh } from "./modes/fresh";
@@ -14,9 +13,6 @@ export {
type BindingMap, type BindingMap,
} from "./bindings"; } from "./bindings";
export { constants } from "./config"; export { constants } from "./config";
export const plugins = {
imageOptimization: ImageOptimizationPlugin,
};
export function d1(config: D1ConnectionConfig) { export function d1(config: D1ConnectionConfig) {
return new D1Connection(config); return new D1Connection(config);

View File

@@ -56,7 +56,7 @@ export class EntityTypescript {
return this.em.entities.map((e) => e.toTypes()); return this.em.entities.map((e) => e.toTypes());
} }
protected getTab(count = 1) { getTab(count = 1) {
return this.options.indentChar.repeat(this.options.indentWidth).repeat(count); return this.options.indentChar.repeat(this.options.indentWidth).repeat(count);
} }

View File

@@ -317,6 +317,7 @@ export class SystemController extends Controller {
local: datetimeStringLocal(), local: datetimeStringLocal(),
utc: datetimeStringUTC(), utc: datetimeStringUTC(),
}, },
plugins: this.app.plugins.map((p) => p.name),
}), }),
); );

View File

@@ -1,18 +1,18 @@
import type { App, AppPlugin } from "bknd"; import type { App, AppPlugin } from "bknd";
export type ImageOptimizationPluginOptions = { export type CloudflareImageOptimizationOptions = {
accessUrl?: string; accessUrl?: string;
resolvePath?: string; resolvePath?: string;
autoFormat?: boolean; autoFormat?: boolean;
devBypass?: string; devBypass?: string;
}; };
export function ImageOptimizationPlugin({ export function cloudflareImageOptimization({
accessUrl = "/_plugin/image/optimize", accessUrl = "/_plugin/image/optimize",
resolvePath = "/api/media/file", resolvePath = "/api/media/file",
autoFormat = true, autoFormat = true,
devBypass, devBypass,
}: ImageOptimizationPluginOptions = {}): AppPlugin { }: CloudflareImageOptimizationOptions = {}): AppPlugin {
const disallowedAccessUrls = ["/api", "/admin", "/_optimize"]; const disallowedAccessUrls = ["/api", "/admin", "/_optimize"];
if (disallowedAccessUrls.includes(accessUrl) || accessUrl.length < 2) { if (disallowedAccessUrls.includes(accessUrl) || accessUrl.length < 2) {
throw new Error(`Disallowed accessUrl: ${accessUrl}`); throw new Error(`Disallowed accessUrl: ${accessUrl}`);

View File

@@ -0,0 +1,18 @@
import type { App, AppPlugin } from "bknd";
import { showRoutes as showRoutesHono } from "hono/dev";
export type ShowRoutesOptions = {
once?: boolean;
};
export function showRoutes({ once = false }: ShowRoutesOptions = {}): AppPlugin {
let shown = false;
return (app: App) => ({
name: "bknd-show-routes",
onBuilt: () => {
if (once && shown) return;
shown = true;
showRoutesHono(app.server);
},
});
}

View File

@@ -0,0 +1,35 @@
import { App, type AppConfig, type AppPlugin } from "bknd";
export type SyncConfigOptions = {
enabled?: boolean;
includeSecrets?: boolean;
write: (config: AppConfig) => Promise<void>;
};
export function syncConfig({
enabled = true,
includeSecrets = false,
write,
}: SyncConfigOptions): AppPlugin {
let firstBoot = true;
return (app: App) => ({
name: "bknd-sync-config",
onBuilt: async () => {
if (!enabled) return;
app.emgr.onEvent(
App.Events.AppConfigUpdatedEvent,
async () => {
await write?.(app.toJSON(includeSecrets));
},
{
id: "sync-config",
},
);
if (firstBoot) {
firstBoot = false;
await write?.(app.toJSON(true));
}
},
});
}

View File

@@ -0,0 +1,31 @@
import { App, type AppPlugin } from "bknd";
import { EntityTypescript } from "data/entities/EntityTypescript";
export type SyncTypesOptions = {
enabled?: boolean;
write: (et: EntityTypescript) => Promise<void>;
};
export function syncTypes({ enabled = true, write }: SyncTypesOptions): AppPlugin {
let firstBoot = true;
return (app: App) => ({
name: "bknd-sync-types",
onBuilt: async () => {
if (!enabled) return;
app.emgr.onEvent(
App.Events.AppConfigUpdatedEvent,
async () => {
await write?.(new EntityTypescript(app.em));
},
{
id: "sync-types",
},
);
if (firstBoot) {
firstBoot = false;
await write?.(new EntityTypescript(app.em));
}
},
});
}

7
app/src/plugins/index.ts Normal file
View File

@@ -0,0 +1,7 @@
export {
cloudflareImageOptimization,
type CloudflareImageOptimizationOptions,
} from "./cloudflare/image-optimization.plugin";
export { showRoutes, type ShowRoutesOptions } from "./dev/show-routes.plugin";
export { syncConfig, type SyncConfigOptions } from "./dev/sync-config.plugin";
export { syncTypes, type SyncTypesOptions } from "./dev/sync-types.plugin";