mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Merge remote-tracking branch 'origin/release/0.17' into feat/mcp
# Conflicts: # app/src/data/AppData.ts # app/src/data/server/query.ts # examples/cloudflare-worker/src/index.ts
This commit is contained in:
@@ -9,16 +9,16 @@ beforeAll(disableConsoleLog);
|
|||||||
afterAll(enableConsoleLog);
|
afterAll(enableConsoleLog);
|
||||||
|
|
||||||
describe("adapter", () => {
|
describe("adapter", () => {
|
||||||
it("makes config", () => {
|
it("makes config", async () => {
|
||||||
expect(omitKeys(adapter.makeConfig({}), ["connection"])).toEqual({});
|
expect(omitKeys(await adapter.makeConfig({}), ["connection"])).toEqual({});
|
||||||
expect(omitKeys(adapter.makeConfig({}, { env: { TEST: "test" } }), ["connection"])).toEqual(
|
expect(
|
||||||
{},
|
omitKeys(await adapter.makeConfig({}, { env: { TEST: "test" } }), ["connection"]),
|
||||||
);
|
).toEqual({});
|
||||||
|
|
||||||
// merges everything returned from `app` with the config
|
// merges everything returned from `app` with the config
|
||||||
expect(
|
expect(
|
||||||
omitKeys(
|
omitKeys(
|
||||||
adapter.makeConfig(
|
await adapter.makeConfig(
|
||||||
{ app: (a) => ({ initialConfig: { server: { cors: { origin: a.env.TEST } } } }) },
|
{ app: (a) => ({ initialConfig: { server: { cors: { origin: a.env.TEST } } } }) },
|
||||||
{ env: { TEST: "test" } },
|
{ env: { TEST: "test" } },
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -256,7 +256,11 @@ async function buildAdapters() {
|
|||||||
),
|
),
|
||||||
tsup.build(baseConfig("astro")),
|
tsup.build(baseConfig("astro")),
|
||||||
tsup.build(baseConfig("aws")),
|
tsup.build(baseConfig("aws")),
|
||||||
tsup.build(baseConfig("cloudflare")),
|
tsup.build(
|
||||||
|
baseConfig("cloudflare", {
|
||||||
|
external: ["wrangler", "node:process"],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
|
||||||
tsup.build({
|
tsup.build({
|
||||||
...baseConfig("vite"),
|
...baseConfig("vite"),
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
"@bluwy/giget-core": "^0.1.2",
|
"@bluwy/giget-core": "^0.1.2",
|
||||||
|
"@clack/prompts": "^0.11.0",
|
||||||
"@cloudflare/vitest-pool-workers": "^0.8.38",
|
"@cloudflare/vitest-pool-workers": "^0.8.38",
|
||||||
"@cloudflare/workers-types": "^4.20250606.0",
|
"@cloudflare/workers-types": "^4.20250606.0",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DataApi, type DataApiOptions } from "data/api/DataApi";
|
|||||||
import { decode } from "hono/jwt";
|
import { decode } from "hono/jwt";
|
||||||
import { MediaApi, type MediaApiOptions } from "media/api/MediaApi";
|
import { MediaApi, type MediaApiOptions } from "media/api/MediaApi";
|
||||||
import { SystemApi } from "modules/SystemApi";
|
import { SystemApi } from "modules/SystemApi";
|
||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "bknd/utils";
|
||||||
import type { BaseModuleApiOptions } from "modules";
|
import type { BaseModuleApiOptions } from "modules";
|
||||||
|
|
||||||
export type TApiUser = SafeUser;
|
export type TApiUser = SafeUser;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CreateUserPayload } from "auth/AppAuth";
|
import type { CreateUserPayload } from "auth/AppAuth";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import { Event } from "core/events";
|
import { Event } from "core/events";
|
||||||
import type { em as prototypeEm } from "data/prototype";
|
import type { em as prototypeEm } from "data/prototype";
|
||||||
import { Connection } from "data/connection/Connection";
|
import { Connection } from "data/connection/Connection";
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { inspect } from "node:util";
|
||||||
|
|
||||||
export type BindingTypeMap = {
|
export type BindingTypeMap = {
|
||||||
D1Database: D1Database;
|
D1Database: D1Database;
|
||||||
KVNamespace: KVNamespace;
|
KVNamespace: KVNamespace;
|
||||||
@@ -13,8 +15,9 @@ export function getBindings<T extends GetBindingType>(env: any, type: T): Bindin
|
|||||||
for (const key in env) {
|
for (const key in env) {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
env[key] &&
|
(env[key] as any).constructor.name === type ||
|
||||||
((env[key] as any).constructor.name === type || String(env[key]) === `[object ${type}]`)
|
String(env[key]) === `[object ${type}]` ||
|
||||||
|
inspect(env[key]).includes(type)
|
||||||
) {
|
) {
|
||||||
bindings.push({
|
bindings.push({
|
||||||
key,
|
key,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe("cf adapter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("makes config", async () => {
|
it("makes config", async () => {
|
||||||
const staticConfig = makeConfig(
|
const staticConfig = await makeConfig(
|
||||||
{
|
{
|
||||||
connection: { url: DB_URL },
|
connection: { url: DB_URL },
|
||||||
initialConfig: { data: { basepath: DB_URL } },
|
initialConfig: { data: { basepath: DB_URL } },
|
||||||
@@ -28,7 +28,7 @@ describe("cf adapter", () => {
|
|||||||
expect(staticConfig.initialConfig).toEqual({ data: { basepath: DB_URL } });
|
expect(staticConfig.initialConfig).toEqual({ data: { basepath: DB_URL } });
|
||||||
expect(staticConfig.connection).toBeDefined();
|
expect(staticConfig.connection).toBeDefined();
|
||||||
|
|
||||||
const dynamicConfig = makeConfig(
|
const dynamicConfig = await makeConfig(
|
||||||
{
|
{
|
||||||
app: (env) => ({
|
app: (env) => ({
|
||||||
initialConfig: { data: { basepath: env.DB_URL } },
|
initialConfig: { data: { basepath: env.DB_URL } },
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import { Hono } from "hono";
|
|||||||
import { serveStatic } from "hono/cloudflare-workers";
|
import { serveStatic } from "hono/cloudflare-workers";
|
||||||
import { getFresh } from "./modes/fresh";
|
import { getFresh } from "./modes/fresh";
|
||||||
import { getCached } from "./modes/cached";
|
import { getCached } from "./modes/cached";
|
||||||
import { getDurable } from "./modes/durable";
|
import type { App, MaybePromise } from "bknd";
|
||||||
import type { App } from "bknd";
|
import { $console } from "bknd/utils";
|
||||||
import { $console } from "core/utils";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Cloudflare {
|
namespace Cloudflare {
|
||||||
@@ -17,12 +16,11 @@ declare global {
|
|||||||
|
|
||||||
export type CloudflareEnv = Cloudflare.Env;
|
export type CloudflareEnv = Cloudflare.Env;
|
||||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
||||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
mode?: "warm" | "fresh" | "cache";
|
||||||
bindings?: (args: Env) => {
|
bindings?: (args: Env) => MaybePromise<{
|
||||||
kv?: KVNamespace;
|
kv?: KVNamespace;
|
||||||
dobj?: DurableObjectNamespace;
|
|
||||||
db?: D1Database;
|
db?: D1Database;
|
||||||
};
|
}>;
|
||||||
d1?: {
|
d1?: {
|
||||||
session?: boolean;
|
session?: boolean;
|
||||||
transport?: "header" | "cookie";
|
transport?: "header" | "cookie";
|
||||||
@@ -93,8 +91,6 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
case "cache":
|
case "cache":
|
||||||
app = await getCached(config, context);
|
app = await getCached(config, context);
|
||||||
break;
|
break;
|
||||||
case "durable":
|
|
||||||
return await getDurable(config, context);
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown mode ${mode}`);
|
throw new Error(`Unknown mode ${mode}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { d1Sqlite } from "./connection/D1Connection";
|
|||||||
import type { CloudflareBkndConfig, CloudflareEnv } from ".";
|
import type { CloudflareBkndConfig, CloudflareEnv } from ".";
|
||||||
import { App } from "bknd";
|
import { App } from "bknd";
|
||||||
import type { Context, ExecutionContext } from "hono";
|
import type { Context, ExecutionContext } from "hono";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import { setCookie } from "hono/cookie";
|
import { setCookie } from "hono/cookie";
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
@@ -89,7 +89,7 @@ export function d1SessionHelper(config: CloudflareBkndConfig<any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let media_registered: boolean = false;
|
let media_registered: boolean = false;
|
||||||
export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
export async function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
config: CloudflareBkndConfig<Env>,
|
config: CloudflareBkndConfig<Env>,
|
||||||
args?: CfMakeConfigArgs<Env>,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
) {
|
) {
|
||||||
@@ -102,7 +102,7 @@ export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
media_registered = true;
|
media_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const appConfig = makeAdapterConfig(config, args?.env);
|
const appConfig = await makeAdapterConfig(config, args?.env);
|
||||||
|
|
||||||
// if connection instance is given, don't do anything
|
// if connection instance is given, don't do anything
|
||||||
// other than checking if D1 session is defined
|
// other than checking if D1 session is defined
|
||||||
@@ -115,12 +115,12 @@ export function makeConfig<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
}
|
}
|
||||||
// if connection is given, try to open with unified sqlite adapter
|
// if connection is given, try to open with unified sqlite adapter
|
||||||
} else if (appConfig.connection) {
|
} else if (appConfig.connection) {
|
||||||
appConfig.connection = sqlite(appConfig.connection);
|
appConfig.connection = sqlite(appConfig.connection) as any;
|
||||||
|
|
||||||
// if connection is not given, but env is set
|
// if connection is not given, but env is set
|
||||||
// try to make D1 from bindings
|
// try to make D1 from bindings
|
||||||
} else if (args?.env) {
|
} else if (args?.env) {
|
||||||
const bindings = config.bindings?.(args?.env);
|
const bindings = await config.bindings?.(args?.env);
|
||||||
const sessionHelper = d1SessionHelper(config);
|
const sessionHelper = d1SessionHelper(config);
|
||||||
const sessionId = sessionHelper.get(args.request);
|
const sessionId = sessionHelper.get(args.request);
|
||||||
let session: D1DatabaseSession | undefined;
|
let session: D1DatabaseSession | undefined;
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
import { genericSqlite, type GenericSqliteConnection } from "bknd";
|
import { genericSqlite, type GenericSqliteConnection } from "bknd";
|
||||||
import type { QueryResult } from "kysely";
|
import type { QueryResult } from "kysely";
|
||||||
|
|
||||||
export type D1SqliteConnection = GenericSqliteConnection<D1Database>;
|
export type DoSqliteConnection = GenericSqliteConnection<DurableObjectState["storage"]["sql"]>;
|
||||||
export type DurableObjecSql = DurableObjectState["storage"]["sql"];
|
export type DurableObjecSql = DurableObjectState["storage"]["sql"];
|
||||||
|
|
||||||
export type D1ConnectionConfig<DB extends DurableObjecSql> =
|
export type DoConnectionConfig<DB extends DurableObjecSql> =
|
||||||
| DurableObjectState
|
| DurableObjectState
|
||||||
| {
|
| {
|
||||||
sql: DB;
|
sql: DB;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function doSqlite<DB extends DurableObjecSql>(config: D1ConnectionConfig<DB>) {
|
export function doSqlite<DB extends DurableObjecSql>(config: DoConnectionConfig<DB>) {
|
||||||
const db = "sql" in config ? config.sql : config.storage.sql;
|
const db = "sql" in config ? config.sql : config.storage.sql;
|
||||||
|
|
||||||
return genericSqlite(
|
return genericSqlite(
|
||||||
@@ -21,7 +21,7 @@ export function doSqlite<DB extends DurableObjecSql>(config: D1ConnectionConfig<
|
|||||||
(utils) => {
|
(utils) => {
|
||||||
// must be async to work with the miniflare mock
|
// must be async to work with the miniflare mock
|
||||||
const getStmt = async (sql: string, parameters?: any[] | readonly any[]) =>
|
const getStmt = async (sql: string, parameters?: any[] | readonly any[]) =>
|
||||||
await db.exec(sql, ...(parameters || []));
|
db.exec(sql, ...(parameters || []));
|
||||||
|
|
||||||
const mapResult = (
|
const mapResult = (
|
||||||
cursor: SqlStorageCursor<Record<string, SqlStorageValue>>,
|
cursor: SqlStorageCursor<Record<string, SqlStorageValue>>,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { d1Sqlite, type D1ConnectionConfig } from "./connection/D1Connection";
|
|||||||
export * from "./cloudflare-workers.adapter";
|
export * from "./cloudflare-workers.adapter";
|
||||||
export { makeApp, getFresh } from "./modes/fresh";
|
export { makeApp, getFresh } from "./modes/fresh";
|
||||||
export { getCached } from "./modes/cached";
|
export { getCached } from "./modes/cached";
|
||||||
export { DurableBkndApp, getDurable } from "./modes/durable";
|
|
||||||
export { d1Sqlite, type D1ConnectionConfig };
|
export { d1Sqlite, type D1ConnectionConfig };
|
||||||
|
export { doSqlite, type DoConnectionConfig } from "./connection/DoConnection";
|
||||||
export {
|
export {
|
||||||
getBinding,
|
getBinding,
|
||||||
getBindings,
|
getBindings,
|
||||||
@@ -15,6 +15,7 @@ export {
|
|||||||
export { constants } from "./config";
|
export { constants } from "./config";
|
||||||
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
||||||
export { registries } from "bknd";
|
export { registries } from "bknd";
|
||||||
|
export { withPlatformProxy } from "./proxy";
|
||||||
|
|
||||||
// for compatibility with old code
|
// for compatibility with old code
|
||||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
args: Context<Env>,
|
args: Context<Env>,
|
||||||
) {
|
) {
|
||||||
const { env, ctx } = args;
|
const { env, ctx } = args;
|
||||||
const { kv } = config.bindings?.(env)!;
|
const { kv } = await 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.key ?? "app";
|
const key = config.key ?? "app";
|
||||||
|
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
import { DurableObject } from "cloudflare:workers";
|
|
||||||
import type { App, CreateAppConfig } from "bknd";
|
|
||||||
import { createRuntimeApp, makeConfig } from "bknd/adapter";
|
|
||||||
import type { CloudflareBkndConfig, Context, CloudflareEnv } from "../index";
|
|
||||||
import { constants, registerAsyncsExecutionContext } from "../config";
|
|
||||||
import { $console } from "core/utils";
|
|
||||||
|
|
||||||
export async function getDurable<Env extends CloudflareEnv = CloudflareEnv>(
|
|
||||||
config: CloudflareBkndConfig<Env>,
|
|
||||||
ctx: Context<Env>,
|
|
||||||
) {
|
|
||||||
const { dobj } = config.bindings?.(ctx.env)!;
|
|
||||||
if (!dobj) throw new Error("durable object is not defined in cloudflare.bindings");
|
|
||||||
const key = config.key ?? "app";
|
|
||||||
|
|
||||||
if ([config.onBuilt, config.beforeBuild].some((x) => x)) {
|
|
||||||
$console.warn("onBuilt and beforeBuild are not supported with DurableObject mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = performance.now();
|
|
||||||
|
|
||||||
const id = dobj.idFromName(key);
|
|
||||||
const stub = dobj.get(id) as unknown as DurableBkndApp;
|
|
||||||
|
|
||||||
const create_config = makeConfig(config, ctx.env);
|
|
||||||
|
|
||||||
const res = await stub.fire(ctx.request, {
|
|
||||||
config: create_config,
|
|
||||||
keepAliveSeconds: config.keepAliveSeconds,
|
|
||||||
});
|
|
||||||
|
|
||||||
const headers = new Headers(res.headers);
|
|
||||||
headers.set("X-TTDO", String(performance.now() - start));
|
|
||||||
|
|
||||||
return new Response(res.body, {
|
|
||||||
status: res.status,
|
|
||||||
statusText: res.statusText,
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DurableBkndApp extends DurableObject {
|
|
||||||
protected id = Math.random().toString(36).slice(2);
|
|
||||||
protected app?: App;
|
|
||||||
protected interval?: any;
|
|
||||||
|
|
||||||
async fire(
|
|
||||||
request: Request,
|
|
||||||
options: {
|
|
||||||
config: CreateAppConfig;
|
|
||||||
html?: string;
|
|
||||||
keepAliveSeconds?: number;
|
|
||||||
setAdminHtml?: boolean;
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
let buildtime = 0;
|
|
||||||
if (!this.app) {
|
|
||||||
const start = performance.now();
|
|
||||||
const config = options.config;
|
|
||||||
|
|
||||||
// change protocol to websocket if libsql
|
|
||||||
if (
|
|
||||||
config?.connection &&
|
|
||||||
"type" in config.connection &&
|
|
||||||
config.connection.type === "libsql"
|
|
||||||
) {
|
|
||||||
//config.connection.config.protocol = "wss";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.app = await createRuntimeApp({
|
|
||||||
...config,
|
|
||||||
onBuilt: async (app) => {
|
|
||||||
registerAsyncsExecutionContext(app, this.ctx);
|
|
||||||
app.modules.server.get(constants.do_endpoint, async (c) => {
|
|
||||||
// @ts-ignore
|
|
||||||
const context: any = c.req.raw.cf ? c.req.raw.cf : c.env.cf;
|
|
||||||
return c.json({
|
|
||||||
id: this.id,
|
|
||||||
keepAliveSeconds: options?.keepAliveSeconds ?? 0,
|
|
||||||
colo: context.colo,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.onBuilt(app);
|
|
||||||
},
|
|
||||||
adminOptions: { html: options.html },
|
|
||||||
beforeBuild: async (app) => {
|
|
||||||
await this.beforeBuild(app);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
buildtime = performance.now() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options?.keepAliveSeconds) {
|
|
||||||
this.keepAlive(options.keepAliveSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await this.app!.fetch(request);
|
|
||||||
const headers = new Headers(res.headers);
|
|
||||||
headers.set("X-BuildTime", buildtime.toString());
|
|
||||||
headers.set("X-DO-ID", this.id);
|
|
||||||
|
|
||||||
return new Response(res.body, {
|
|
||||||
status: res.status,
|
|
||||||
statusText: res.statusText,
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async onBuilt(app: App) {}
|
|
||||||
|
|
||||||
async beforeBuild(app: App) {}
|
|
||||||
|
|
||||||
protected keepAlive(seconds: number) {
|
|
||||||
if (this.interval) {
|
|
||||||
clearInterval(this.interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
i += 1;
|
|
||||||
if (i === seconds) {
|
|
||||||
console.log("cleared");
|
|
||||||
clearInterval(this.interval);
|
|
||||||
|
|
||||||
// ping every 30 seconds
|
|
||||||
} else if (i % 30 === 0) {
|
|
||||||
console.log("ping");
|
|
||||||
this.app?.modules.ctx().connection.ping();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ export async function makeApp<Env extends CloudflareEnv = CloudflareEnv>(
|
|||||||
args?: CfMakeConfigArgs<Env>,
|
args?: CfMakeConfigArgs<Env>,
|
||||||
opts?: RuntimeOptions,
|
opts?: RuntimeOptions,
|
||||||
) {
|
) {
|
||||||
return await createRuntimeApp<Env>(makeConfig(config, args), args?.env, opts);
|
return await createRuntimeApp<Env>(await makeConfig(config, args), args?.env, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||||
|
|||||||
66
app/src/adapter/cloudflare/proxy.ts
Normal file
66
app/src/adapter/cloudflare/proxy.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
d1Sqlite,
|
||||||
|
getBinding,
|
||||||
|
registerMedia,
|
||||||
|
type CloudflareBkndConfig,
|
||||||
|
type CloudflareEnv,
|
||||||
|
} from ".";
|
||||||
|
import type { PlatformProxy } from "wrangler";
|
||||||
|
import process from "node:process";
|
||||||
|
|
||||||
|
export type WithPlatformProxyOptions = {
|
||||||
|
/**
|
||||||
|
* By default, proxy is used if the PROXY environment variable is set to 1.
|
||||||
|
* You can override/force this by setting this option.
|
||||||
|
*/
|
||||||
|
useProxy?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function withPlatformProxy<Env extends CloudflareEnv>(
|
||||||
|
config?: CloudflareBkndConfig<Env>,
|
||||||
|
opts?: WithPlatformProxyOptions,
|
||||||
|
) {
|
||||||
|
const use_proxy =
|
||||||
|
typeof opts?.useProxy === "boolean" ? opts.useProxy : process.env.PROXY === "1";
|
||||||
|
let proxy: PlatformProxy | undefined;
|
||||||
|
|
||||||
|
async function getEnv(env?: Env): Promise<Env> {
|
||||||
|
if (use_proxy) {
|
||||||
|
if (!proxy) {
|
||||||
|
const getPlatformProxy = await import("wrangler").then((mod) => mod.getPlatformProxy);
|
||||||
|
proxy = await getPlatformProxy();
|
||||||
|
setTimeout(proxy?.dispose, 1000);
|
||||||
|
}
|
||||||
|
return proxy.env as unknown as Env;
|
||||||
|
}
|
||||||
|
return env || ({} as Env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
beforeBuild: async (app, registries) => {
|
||||||
|
if (!use_proxy) return;
|
||||||
|
const env = await getEnv();
|
||||||
|
registerMedia(env, registries);
|
||||||
|
await config?.beforeBuild?.(app, registries);
|
||||||
|
},
|
||||||
|
bindings: async (env) => {
|
||||||
|
return (await config?.bindings?.(await getEnv(env))) || {};
|
||||||
|
},
|
||||||
|
app: async (_env) => {
|
||||||
|
const env = await getEnv(_env);
|
||||||
|
|
||||||
|
if (config?.app === undefined && use_proxy) {
|
||||||
|
const binding = getBinding(env, "D1Database");
|
||||||
|
return {
|
||||||
|
connection: d1Sqlite({
|
||||||
|
binding: binding.value,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
} else if (typeof config?.app === "function") {
|
||||||
|
return config?.app(env);
|
||||||
|
}
|
||||||
|
return config?.app || {};
|
||||||
|
},
|
||||||
|
} satisfies CloudflareBkndConfig<Env>;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { registries, isDebug, guessMimeType } from "bknd";
|
import { registries as $registries, isDebug, guessMimeType } from "bknd";
|
||||||
import { getBindings } from "../bindings";
|
import { getBindings } from "../bindings";
|
||||||
import { s } from "bknd/utils";
|
import { s } from "bknd/utils";
|
||||||
import { StorageAdapter, type FileBody } from "bknd";
|
import { StorageAdapter, type FileBody } from "bknd";
|
||||||
@@ -12,7 +12,10 @@ export function makeSchema(bindings: string[] = []) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerMedia(env: Record<string, any>) {
|
export function registerMedia(
|
||||||
|
env: Record<string, any>,
|
||||||
|
registries: typeof $registries = $registries,
|
||||||
|
) {
|
||||||
const r2_bindings = getBindings(env, "R2Bucket");
|
const r2_bindings = getBindings(env, "R2Bucket");
|
||||||
|
|
||||||
registries.media.register(
|
registries.media.register(
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { config as $config, App, type CreateAppConfig, Connection, guessMimeType } from "bknd";
|
import {
|
||||||
|
config as $config,
|
||||||
|
App,
|
||||||
|
type CreateAppConfig,
|
||||||
|
Connection,
|
||||||
|
guessMimeType,
|
||||||
|
type MaybePromise,
|
||||||
|
registries as $registries,
|
||||||
|
} from "bknd";
|
||||||
import { $console } from "bknd/utils";
|
import { $console } from "bknd/utils";
|
||||||
import type { Context, MiddlewareHandler, Next } from "hono";
|
import type { Context, MiddlewareHandler, Next } from "hono";
|
||||||
import type { AdminControllerOptions } from "modules/server/AdminController";
|
import type { AdminControllerOptions } from "modules/server/AdminController";
|
||||||
import type { Manifest } from "vite";
|
import type { Manifest } from "vite";
|
||||||
|
|
||||||
export type BkndConfig<Args = any> = CreateAppConfig & {
|
export type BkndConfig<Args = any> = CreateAppConfig & {
|
||||||
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
|
app?: CreateAppConfig | ((args: Args) => MaybePromise<CreateAppConfig>);
|
||||||
onBuilt?: (app: App) => Promise<void>;
|
onBuilt?: (app: App) => Promise<void>;
|
||||||
beforeBuild?: (app: App) => Promise<void>;
|
beforeBuild?: (app: App, registries?: typeof $registries) => Promise<void>;
|
||||||
buildConfig?: Parameters<App["build"]>[0];
|
buildConfig?: Parameters<App["build"]>[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,10 +38,10 @@ export type DefaultArgs = {
|
|||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function makeConfig<Args = DefaultArgs>(
|
export async function makeConfig<Args = DefaultArgs>(
|
||||||
config: BkndConfig<Args>,
|
config: BkndConfig<Args>,
|
||||||
args?: Args,
|
args?: Args,
|
||||||
): CreateAppConfig {
|
): Promise<CreateAppConfig> {
|
||||||
let additionalConfig: CreateAppConfig = {};
|
let additionalConfig: CreateAppConfig = {};
|
||||||
const { app, ...rest } = config;
|
const { app, ...rest } = config;
|
||||||
if (app) {
|
if (app) {
|
||||||
@@ -41,7 +49,7 @@ export function makeConfig<Args = DefaultArgs>(
|
|||||||
if (!args) {
|
if (!args) {
|
||||||
throw new Error("args is required when config.app is a function");
|
throw new Error("args is required when config.app is a function");
|
||||||
}
|
}
|
||||||
additionalConfig = app(args);
|
additionalConfig = await app(args);
|
||||||
} else {
|
} else {
|
||||||
additionalConfig = app;
|
additionalConfig = app;
|
||||||
}
|
}
|
||||||
@@ -60,7 +68,7 @@ export async function createAdapterApp<Config extends BkndConfig = BkndConfig, A
|
|||||||
const id = opts?.id ?? "app";
|
const id = opts?.id ?? "app";
|
||||||
let app = apps.get(id);
|
let app = apps.get(id);
|
||||||
if (!app || opts?.force) {
|
if (!app || opts?.force) {
|
||||||
const appConfig = makeConfig(config, args);
|
const appConfig = await makeConfig(config, args);
|
||||||
if (!appConfig.connection || !Connection.isConnection(appConfig.connection)) {
|
if (!appConfig.connection || !Connection.isConnection(appConfig.connection)) {
|
||||||
let connection: Connection | undefined;
|
let connection: Connection | undefined;
|
||||||
if (Connection.isConnection(config.connection)) {
|
if (Connection.isConnection(config.connection)) {
|
||||||
@@ -68,7 +76,7 @@ export async function createAdapterApp<Config extends BkndConfig = BkndConfig, A
|
|||||||
} else {
|
} else {
|
||||||
const sqlite = (await import("bknd/adapter/sqlite")).sqlite;
|
const sqlite = (await import("bknd/adapter/sqlite")).sqlite;
|
||||||
const conf = appConfig.connection ?? { url: ":memory:" };
|
const conf = appConfig.connection ?? { url: ":memory:" };
|
||||||
connection = sqlite(conf);
|
connection = sqlite(conf) as any;
|
||||||
$console.info(`Using ${connection!.name} connection`, conf.url);
|
$console.info(`Using ${connection!.name} connection`, conf.url);
|
||||||
}
|
}
|
||||||
appConfig.connection = connection;
|
appConfig.connection = connection;
|
||||||
@@ -98,7 +106,7 @@ export async function createFrameworkApp<Args = DefaultArgs>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await config.beforeBuild?.(app);
|
await config.beforeBuild?.(app, $registries);
|
||||||
await app.build(config.buildConfig);
|
await app.build(config.buildConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +139,7 @@ export async function createRuntimeApp<Args = DefaultArgs>(
|
|||||||
"sync",
|
"sync",
|
||||||
);
|
);
|
||||||
|
|
||||||
await config.beforeBuild?.(app);
|
await config.beforeBuild?.(app, $registries);
|
||||||
await app.build(config.buildConfig);
|
await app.build(config.buildConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { DB, PrimaryFieldType } from "bknd";
|
|||||||
import * as AuthPermissions from "auth/auth-permissions";
|
import * as AuthPermissions from "auth/auth-permissions";
|
||||||
import type { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
import type { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
||||||
import type { PasswordStrategy } from "auth/authenticate/strategies/PasswordStrategy";
|
import type { PasswordStrategy } from "auth/authenticate/strategies/PasswordStrategy";
|
||||||
import { $console, secureRandomString, transformObject } from "core/utils";
|
import { $console, secureRandomString, transformObject } from "bknd/utils";
|
||||||
import type { Entity, EntityManager } from "data/entities";
|
import type { Entity, EntityManager } from "data/entities";
|
||||||
import { em, entity, enumm, type FieldSchema } from "data/prototype";
|
import { em, entity, enumm, type FieldSchema } from "data/prototype";
|
||||||
import { Module } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AppAuth } from "auth/AppAuth";
|
import { AppAuth } from "auth/AppAuth";
|
||||||
import type { CreateUser, SafeUser, User, UserPool } from "auth/authenticate/Authenticator";
|
import type { CreateUser, SafeUser, User, UserPool } from "auth/authenticate/Authenticator";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import { pick } from "lodash-es";
|
import { pick } from "lodash-es";
|
||||||
import {
|
import {
|
||||||
InvalidConditionsException,
|
InvalidConditionsException,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import type { User } from "bknd";
|
import type { User } from "bknd";
|
||||||
import type { Authenticator } from "auth/authenticate/Authenticator";
|
import type { Authenticator } from "auth/authenticate/Authenticator";
|
||||||
import { InvalidCredentialsException } from "auth/errors";
|
import { InvalidCredentialsException } from "auth/errors";
|
||||||
import { hash, $console } from "core/utils";
|
import { hash, $console, s, parse, jsc } from "bknd/utils";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { compare as bcryptCompare, genSalt as bcryptGenSalt, hash as bcryptHash } from "bcryptjs";
|
import { compare as bcryptCompare, genSalt as bcryptGenSalt, hash as bcryptHash } from "bcryptjs";
|
||||||
import { AuthStrategy } from "./Strategy";
|
import { AuthStrategy } from "./Strategy";
|
||||||
import { s, parse, jsc } from "bknd/utils";
|
|
||||||
|
|
||||||
const schema = s
|
const schema = s
|
||||||
.object({
|
.object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Exception } from "core/errors";
|
import { Exception } from "core/errors";
|
||||||
import { $console, objectTransform } from "core/utils";
|
import { $console, objectTransform } from "bknd/utils";
|
||||||
import { Permission } from "core/security/Permission";
|
import { Permission } from "core/security/Permission";
|
||||||
import type { Context } from "hono";
|
import type { Context } from "hono";
|
||||||
import type { ServerEnv } from "modules/Controller";
|
import type { ServerEnv } from "modules/Controller";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as $p from "@clack/prompts";
|
import * as $p from "@clack/prompts";
|
||||||
import { overrideJson, overridePackageJson } from "cli/commands/create/npm";
|
import { overrideJson } from "cli/commands/create/npm";
|
||||||
import { typewriter, wait } from "cli/utils/cli";
|
import { typewriter } from "cli/utils/cli";
|
||||||
import { uuid } from "core/utils";
|
|
||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
import type { Template, TemplateSetupCtx } from ".";
|
import type { Template, TemplateSetupCtx } from ".";
|
||||||
import { exec } from "cli/utils/sys";
|
import { exec } from "cli/utils/sys";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import type { MiddlewareHandler } from "hono";
|
import type { MiddlewareHandler } from "hono";
|
||||||
import open from "open";
|
import open from "open";
|
||||||
import { fileExists, getRelativeDistPath } from "../../utils/sys";
|
import { fileExists, getRelativeDistPath } from "../../utils/sys";
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ async function makeApp(config: MakeAppConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
||||||
const config = makeConfig(_config, process.env);
|
const config = await makeConfig(_config, process.env);
|
||||||
return makeApp({
|
return makeApp({
|
||||||
...config,
|
...config,
|
||||||
server: { platform },
|
server: { platform },
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import type { PasswordStrategy } from "auth/authenticate/strategies";
|
|||||||
import { makeAppFromEnv } from "cli/commands/run";
|
import { makeAppFromEnv } from "cli/commands/run";
|
||||||
import type { CliCommand } from "cli/types";
|
import type { CliCommand } from "cli/types";
|
||||||
import { Argument } from "commander";
|
import { Argument } from "commander";
|
||||||
import { $console } from "core/utils";
|
import { $console, isBun } from "bknd/utils";
|
||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
import { isBun } from "core/utils";
|
|
||||||
|
|
||||||
export const user: CliCommand = (program) => {
|
export const user: CliCommand = (program) => {
|
||||||
program
|
program
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import { execSync, exec as nodeExec } from "node:child_process";
|
import { execSync, exec as nodeExec } from "node:child_process";
|
||||||
import { readFile, writeFile as nodeWriteFile } from "node:fs/promises";
|
import { readFile, writeFile as nodeWriteFile } from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { mergeObject, type RecursivePartial } from "core/utils";
|
import { mergeObject, type RecursivePartial } from "bknd/utils";
|
||||||
import type { IEmailDriver } from "./index";
|
import type { IEmailDriver } from "./index";
|
||||||
|
|
||||||
export type MailchannelsEmailOptions = {
|
export type MailchannelsEmailOptions = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type Event, type EventClass, InvalidEventReturn } from "./Event";
|
import { type Event, type EventClass, InvalidEventReturn } from "./Event";
|
||||||
import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener";
|
import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
|
|
||||||
export type RegisterListenerConfig =
|
export type RegisterListenerConfig =
|
||||||
| ListenerMode
|
| ListenerMode
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { TestRunner } from "core/test";
|
import type { TestRunner } from "core/test";
|
||||||
import { Connection, type FieldSpec } from "./Connection";
|
import { Connection, type FieldSpec } from "./Connection";
|
||||||
import { getPath } from "core/utils";
|
import { getPath } from "bknd/utils";
|
||||||
import * as proto from "data/prototype";
|
import * as proto from "data/prototype";
|
||||||
import { createApp } from "App";
|
import { createApp } from "App";
|
||||||
import type { MaybePromise } from "core/types";
|
import type { MaybePromise } from "core/types";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { objectTransform } from "core/utils";
|
import { objectTransform } from "bknd/utils";
|
||||||
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
|
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
|
||||||
import { FieldClassMap } from "data/fields";
|
import { FieldClassMap } from "data/fields";
|
||||||
import { RelationClassMap, RelationFieldClassMap } from "data/relations";
|
import { RelationClassMap, RelationFieldClassMap } from "data/relations";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Entity, EntityManager, TEntityType } from "data/entities";
|
import type { Entity, EntityManager, TEntityType } from "data/entities";
|
||||||
import type { EntityRelation } from "data/relations";
|
import type { EntityRelation } from "data/relations";
|
||||||
import { autoFormatString } from "core/utils";
|
import { autoFormatString } from "bknd/utils";
|
||||||
import { usersFields } from "auth/auth-entities";
|
import { usersFields } from "auth/auth-entities";
|
||||||
import { mediaFields } from "media/media-entities";
|
import { mediaFields } from "media/media-entities";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { isDebug } from "core/env";
|
import { isDebug } from "core/env";
|
||||||
import { pick } from "core/utils";
|
import { pick } from "bknd/utils";
|
||||||
import type { Connection } from "data/connection";
|
import type { Connection } from "data/connection";
|
||||||
import type {
|
import type {
|
||||||
Compilable,
|
Compilable,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import type { Entity, EntityData } from "../Entity";
|
import type { Entity, EntityData } from "../Entity";
|
||||||
import type { EntityManager } from "../EntityManager";
|
import type { EntityManager } from "../EntityManager";
|
||||||
import { Result, type ResultJSON, type ResultOptions } from "../Result";
|
import { Result, type ResultJSON, type ResultOptions } from "../Result";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { DB as DefaultDB, PrimaryFieldType } from "bknd";
|
import type { DB as DefaultDB, PrimaryFieldType } from "bknd";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import { type EmitsEvents, EventManager } from "core/events";
|
import { type EmitsEvents, EventManager } from "core/events";
|
||||||
import { type SelectQueryBuilder, sql } from "kysely";
|
import { type SelectQueryBuilder, sql } from "kysely";
|
||||||
import { InvalidSearchParamsException } from "../../errors";
|
import { InvalidSearchParamsException } from "../../errors";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { Entity, EntityData } from "../Entity";
|
|||||||
import type { EntityManager } from "../EntityManager";
|
import type { EntityManager } from "../EntityManager";
|
||||||
import { Result, type ResultJSON, type ResultOptions } from "../Result";
|
import { Result, type ResultJSON, type ResultOptions } from "../Result";
|
||||||
import type { Compilable, SelectQueryBuilder } from "kysely";
|
import type { Compilable, SelectQueryBuilder } from "kysely";
|
||||||
import { $console, ensureInt } from "core/utils";
|
import { $console, ensureInt } from "bknd/utils";
|
||||||
|
|
||||||
export type RepositoryResultOptions = ResultOptions & {
|
export type RepositoryResultOptions = ResultOptions & {
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isObject } from "core/utils";
|
import { isObject } from "bknd/utils";
|
||||||
|
|
||||||
import type { KyselyJsonFrom } from "data/relations/EntityRelation";
|
import type { KyselyJsonFrom } from "data/relations/EntityRelation";
|
||||||
import type { RepoQuery } from "data/server/query";
|
import type { RepoQuery } from "data/server/query";
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { omitKeys } from "core/utils";
|
import { omitKeys, s } from "bknd/utils";
|
||||||
import type { EntityManager } from "data/entities";
|
import type { EntityManager } from "data/entities";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export const booleanFieldConfigSchema = s
|
export const booleanFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { dayjs } from "core/utils";
|
import { dayjs, $console, s } from "bknd/utils";
|
||||||
import type { EntityManager } from "../entities";
|
import type { EntityManager } from "../entities";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { $console } from "core/utils";
|
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export const dateFieldConfigSchema = s
|
export const dateFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "bknd/utils";
|
||||||
import type { EntityManager } from "data/entities";
|
import type { EntityManager } from "data/entities";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { baseFieldConfigSchema, Field, type TActionContext, type TRenderContext } from "./Field";
|
import { baseFieldConfigSchema, Field, type TActionContext, type TRenderContext } from "./Field";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omitKeys } from "core/utils";
|
import { omitKeys } from "bknd/utils";
|
||||||
import type { EntityManager } from "data/entities";
|
import type { EntityManager } from "data/entities";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type Schema as JsonSchema, Validator } from "@cfworker/json-schema";
|
import { type Schema as JsonSchema, Validator } from "@cfworker/json-schema";
|
||||||
import { objectToJsLiteral } from "core/utils";
|
import { objectToJsLiteral } from "bknd/utils";
|
||||||
import type { EntityManager } from "data/entities";
|
import type { EntityManager } from "data/entities";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import type { EntityManager } from "data/entities";
|
|||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema } from "./Field";
|
||||||
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
||||||
import { s } from "bknd/utils";
|
import { s, omitKeys } from "bknd/utils";
|
||||||
import { omitKeys } from "core/utils";
|
|
||||||
|
|
||||||
export const numberFieldConfigSchema = s
|
export const numberFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import type { EntityManager } from "data/entities";
|
import type { EntityManager } from "data/entities";
|
||||||
import { omitKeys } from "core/utils";
|
import { omitKeys, s } from "bknd/utils";
|
||||||
import { TransformPersistFailedException } from "../errors";
|
import { TransformPersistFailedException } from "../errors";
|
||||||
import { Field, type TActionContext, baseFieldConfigSchema } from "./Field";
|
import { Field, type TActionContext, baseFieldConfigSchema } from "./Field";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export const textFieldConfigSchema = s
|
export const textFieldConfigSchema = s
|
||||||
.strictObject({
|
.strictObject({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { transformObject } from "core/utils";
|
import { transformObject } from "bknd/utils";
|
||||||
import { Entity } from "data/entities";
|
import { Entity } from "data/entities";
|
||||||
import type { Field } from "data/fields";
|
import type { Field } from "data/fields";
|
||||||
import { FIELDS, RELATIONS, type TAppDataEntity, type TAppDataRelation } from "data/data-schema";
|
import { FIELDS, RELATIONS, type TAppDataEntity, type TAppDataRelation } from "data/data-schema";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { transformObject } from "core/utils";
|
import { transformObject, s } from "bknd/utils";
|
||||||
import { TaskMap, TriggerMap } from "flows";
|
import { TaskMap, TriggerMap } from "flows";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export const TASKS = {
|
export const TASKS = {
|
||||||
...TaskMap,
|
...TaskMap,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Event, EventManager, type ListenerHandler } from "core/events";
|
|||||||
import type { EmitsEvents } from "core/events";
|
import type { EmitsEvents } from "core/events";
|
||||||
import type { Task, TaskResult } from "../tasks/Task";
|
import type { Task, TaskResult } from "../tasks/Task";
|
||||||
import type { Flow } from "./Flow";
|
import type { Flow } from "./Flow";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
|
|
||||||
export type TaskLog = TaskResult & {
|
export type TaskLog = TaskResult & {
|
||||||
task: Task;
|
task: Task;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { $console, transformObject } from "core/utils";
|
import { $console, transformObject } from "bknd/utils";
|
||||||
import { type TaskMapType, TriggerMap } from "../index";
|
import { type TaskMapType, TriggerMap } from "../index";
|
||||||
import type { Task } from "../tasks/Task";
|
import type { Task } from "../tasks/Task";
|
||||||
import { Condition, TaskConnection } from "../tasks/TaskConnection";
|
import { Condition, TaskConnection } from "../tasks/TaskConnection";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Task } from "../../tasks/Task";
|
import type { Task } from "../../tasks/Task";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
|
|
||||||
export class RuntimeExecutor {
|
export class RuntimeExecutor {
|
||||||
async run(
|
async run(
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import type { EventManager } from "core/events";
|
import type { EventManager } from "core/events";
|
||||||
import type { Flow } from "../Flow";
|
import type { Flow } from "../Flow";
|
||||||
import { Trigger } from "./Trigger";
|
import { Trigger } from "./Trigger";
|
||||||
import { $console } from "core/utils";
|
import { $console, s } from "bknd/utils";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export class EventTrigger extends Trigger<typeof EventTrigger.schema> {
|
export class EventTrigger extends Trigger<typeof EventTrigger.schema> {
|
||||||
override type = "event";
|
override type = "event";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { objectCleanEmpty, uuid } from "core/utils";
|
import { objectCleanEmpty, uuid } from "bknd/utils";
|
||||||
import { get } from "lodash-es";
|
import { get } from "lodash-es";
|
||||||
import type { Task, TaskResult } from "./Task";
|
import type { Task, TaskResult } from "./Task";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Task } from "../Task";
|
import { Task } from "../Task";
|
||||||
import { $console } from "core/utils";
|
import { $console, s } from "bknd/utils";
|
||||||
import { s } from "bknd/utils";
|
|
||||||
|
|
||||||
export class LogTask extends Task<typeof LogTask.schema> {
|
export class LogTask extends Task<typeof LogTask.schema> {
|
||||||
type = "log";
|
type = "log";
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export { registries } from "modules/registries";
|
|||||||
/**
|
/**
|
||||||
* Core
|
* Core
|
||||||
*/
|
*/
|
||||||
|
export type { MaybePromise } from "core/types";
|
||||||
export { Exception, BkndError } from "core/errors";
|
export { Exception, BkndError } from "core/errors";
|
||||||
export { isDebug, env } from "core/env";
|
export { isDebug, env } from "core/env";
|
||||||
export { type PrimaryFieldType, config, type DB, type AppEntity } from "core/config";
|
export { type PrimaryFieldType, config, type DB, type AppEntity } from "core/config";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AppEntity, FileUploadedEventData, StorageAdapter } from "bknd";
|
import type { AppEntity, FileUploadedEventData, StorageAdapter } from "bknd";
|
||||||
import { $console } from "core/utils";
|
import { $console } from "bknd/utils";
|
||||||
import type { Entity, EntityManager } from "data/entities";
|
import type { Entity, EntityManager } from "data/entities";
|
||||||
import { Storage } from "media/storage/Storage";
|
import { Storage } from "media/storage/Storage";
|
||||||
import { Module } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type EmitsEvents, EventManager } from "core/events";
|
import { type EmitsEvents, EventManager } from "core/events";
|
||||||
import { $console, isFile, detectImageDimensions } from "core/utils";
|
import { $console, isFile, detectImageDimensions } from "bknd/utils";
|
||||||
import { isMimeType } from "media/storage/mime-types-tiny";
|
import { isMimeType } from "media/storage/mime-types-tiny";
|
||||||
import * as StorageEvents from "./events";
|
import * as StorageEvents from "./events";
|
||||||
import type { FileUploadedEventData } from "./events";
|
import type { FileUploadedEventData } from "./events";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { hash, pickHeaders } from "core/utils";
|
import { hash, pickHeaders, s, parse } from "bknd/utils";
|
||||||
import type { FileBody, FileListObject, FileMeta } from "../../Storage";
|
import type { FileBody, FileListObject, FileMeta } from "../../Storage";
|
||||||
import { StorageAdapter } from "../../StorageAdapter";
|
import { StorageAdapter } from "../../StorageAdapter";
|
||||||
import { s, parse } from "bknd/utils";
|
|
||||||
|
|
||||||
export const cloudinaryAdapterConfig = s.object(
|
export const cloudinaryAdapterConfig = s.object(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { transformObject } from "core/utils";
|
import { transformObject } from "bknd/utils";
|
||||||
import type { Kysely } from "kysely";
|
import type { Kysely } from "kysely";
|
||||||
import { set } from "lodash-es";
|
import { set } from "lodash-es";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type NotificationData, notifications } from "@mantine/notifications";
|
import { type NotificationData, notifications } from "@mantine/notifications";
|
||||||
import type { Api } from "Api";
|
import type { Api } from "Api";
|
||||||
import { ucFirst } from "core/utils";
|
import { ucFirst } from "bknd/utils";
|
||||||
import type { ModuleConfigs } from "modules";
|
import type { ModuleConfigs } from "modules";
|
||||||
import type { ResponseObject } from "modules/ModuleApi";
|
import type { ResponseObject } from "modules/ModuleApi";
|
||||||
import type { ConfigUpdateResponse } from "modules/server/SystemController";
|
import type { ConfigUpdateResponse } from "modules/server/SystemController";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { autoFormatString, omitKeys } from "core/utils";
|
import { autoFormatString, omitKeys } from "bknd/utils";
|
||||||
import { type Draft, Draft2019, type JsonSchema } from "json-schema-library";
|
import { type Draft, Draft2019, type JsonSchema } from "json-schema-library";
|
||||||
import type { JSONSchema } from "json-schema-to-ts";
|
import type { JSONSchema } from "json-schema-to-ts";
|
||||||
import type { JSONSchemaType } from "json-schema-to-ts/lib/types/definitions/jsonSchema";
|
import type { JSONSchemaType } from "json-schema-to-ts/lib/types/definitions/jsonSchema";
|
||||||
|
|
||||||
export { isEqual, getPath } from "core/utils/objects";
|
export { isEqual, getPath } from "bknd/utils";
|
||||||
|
|
||||||
export function isNotDefined(value: any) {
|
export function isNotDefined(value: any) {
|
||||||
return value === null || value === undefined || value === "";
|
return value === null || value === undefined || value === "";
|
||||||
|
|||||||
5
bun.lock
5
bun.lock
@@ -46,6 +46,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
"@bluwy/giget-core": "^0.1.2",
|
"@bluwy/giget-core": "^0.1.2",
|
||||||
|
"@clack/prompts": "^0.11.0",
|
||||||
"@cloudflare/vitest-pool-workers": "^0.8.38",
|
"@cloudflare/vitest-pool-workers": "^0.8.38",
|
||||||
"@cloudflare/workers-types": "^4.20250606.0",
|
"@cloudflare/workers-types": "^4.20250606.0",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
@@ -503,6 +504,10 @@
|
|||||||
|
|
||||||
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
||||||
|
|
||||||
|
"@clack/core": ["@clack/core@0.5.0", "https://registry.npmmirror.com/@clack/core/-/core-0.5.0.tgz", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
|
||||||
|
|
||||||
|
"@clack/prompts": ["@clack/prompts@0.11.0", "https://registry.npmmirror.com/@clack/prompts/-/prompts-0.11.0.tgz", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
||||||
|
|
||||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
||||||
|
|
||||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="],
|
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Stage 1: Build stage
|
# Stage 1: Build stage
|
||||||
FROM node:20 as builder
|
FROM node:24 as builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ RUN npm install --omit=dev bknd@${VERSION}
|
|||||||
RUN mkdir /output && cp -r node_modules/bknd/dist /output/dist
|
RUN mkdir /output && cp -r node_modules/bknd/dist /output/dist
|
||||||
|
|
||||||
# Stage 2: Final minimal image
|
# Stage 2: Final minimal image
|
||||||
FROM node:20-alpine
|
FROM node:24-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export const prerender = false;
|
|||||||
withProvider={{ user }}
|
withProvider={{ user }}
|
||||||
config={{
|
config={{
|
||||||
basepath: "/admin",
|
basepath: "/admin",
|
||||||
color_scheme: "dark",
|
theme: "dark",
|
||||||
logo_return_path: "/../"
|
logo_return_path: "/../"
|
||||||
}}
|
}}
|
||||||
client:only
|
client:only
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export default async function AdminPage() {
|
|||||||
config={{
|
config={{
|
||||||
basepath: "/admin",
|
basepath: "/admin",
|
||||||
logo_return_path: "/../",
|
logo_return_path: "/../",
|
||||||
color_scheme: "system",
|
theme: "system",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
2
examples/cloudflare-worker/.gitignore
vendored
2
examples/cloudflare-worker/.gitignore
vendored
@@ -170,3 +170,5 @@ dist
|
|||||||
|
|
||||||
.dev.vars
|
.dev.vars
|
||||||
.wrangler/
|
.wrangler/
|
||||||
|
bknd-types.d.ts
|
||||||
|
worker-configuration.d.ts
|
||||||
12
examples/cloudflare-worker/bknd.config.ts
Normal file
12
examples/cloudflare-worker/bknd.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Optionally wrapping the configuration with the `withPlatformProxy` function
|
||||||
|
* enables programmatic access to the bindings, e.g. for generating types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { withPlatformProxy } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
|
export default withPlatformProxy({
|
||||||
|
d1: {
|
||||||
|
session: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -2,16 +2,19 @@
|
|||||||
"name": "cloudflare-worker",
|
"name": "cloudflare-worker",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "wrangler deploy",
|
"deploy": "wrangler deploy",
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
"typegen": "wrangler types"
|
"bknd-typegen": "PROXY=1 npx bknd types",
|
||||||
|
"typegen": "wrangler types && npm run bknd-typegen",
|
||||||
|
"predev": "npm run typegen"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bknd": "file:../../app"
|
"bknd": "file:../../app"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.9.2",
|
||||||
"wrangler": "^4.19.1"
|
"wrangler": "^4.28.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
import config from "../bknd.config";
|
||||||
|
|
||||||
export default serve({
|
export default serve(config);
|
||||||
mode: "fresh",
|
|
||||||
d1: {
|
|
||||||
session: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2021",
|
"lib": ["ES2022"],
|
||||||
"lib": ["es2021"],
|
"target": "ES2022",
|
||||||
"jsx": "react-jsx",
|
"module": "ES2022",
|
||||||
"module": "es2022",
|
"moduleResolution": "bundler",
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"types": ["./worker-configuration.d.ts"],
|
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
@@ -18,5 +16,10 @@
|
|||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"exclude": ["test"],
|
"exclude": ["test"],
|
||||||
"include": ["worker-configuration.d.ts", "src/**/*.ts"]
|
"include": [
|
||||||
|
"worker-configuration.d.ts",
|
||||||
|
"bknd-types.d.ts",
|
||||||
|
"bknd.config.ts",
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// placeholder, run generation again
|
|
||||||
declare namespace Cloudflare {
|
|
||||||
interface Env {
|
|
||||||
BUCKET: R2Bucket;
|
|
||||||
DB: D1Database;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interface Env extends Cloudflare.Env {}
|
|
||||||
Reference in New Issue
Block a user