mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-04 00:56:01 +00:00
feat: remove cloudflare adapter dependency to nodejs_compat
- adjusted bindings extraction to not rely on node inspect anymore - added new vite export that uses nodejs apis
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { inspect } from "node:util";
|
||||
|
||||
export type BindingTypeMap = {
|
||||
D1Database: D1Database;
|
||||
KVNamespace: KVNamespace;
|
||||
@@ -10,21 +8,40 @@ export type BindingTypeMap = {
|
||||
export type GetBindingType = keyof BindingTypeMap;
|
||||
export type BindingMap<T extends GetBindingType> = { key: string; value: BindingTypeMap[T] };
|
||||
|
||||
export const bindingMatchers = {
|
||||
// D1Database instance doesn't stringify to [object D1Database] (yet)
|
||||
D1Database: (value: object): value is D1Database => {
|
||||
if (typeof value !== "object" || value === null) return false;
|
||||
if (String(value) === "[object D1Database]") return true;
|
||||
if (Object.keys(value).length !== 2) return false;
|
||||
return ["alwaysPrimarySession", "fetcher"].every((key) => key in value);
|
||||
},
|
||||
KVNamespace: (value: object): value is KVNamespace =>
|
||||
String(value) === "[object KvNamespace]" && Object.keys(value).length === 0,
|
||||
DurableObjectNamespace: (value: object): value is DurableObjectNamespace =>
|
||||
String(value) === "[object DurableObjectNamespace]" && Object.keys(value).length === 0,
|
||||
R2Bucket: (value: object): value is R2Bucket =>
|
||||
String(value) === "[object R2Bucket]" && Object.keys(value).length === 0,
|
||||
};
|
||||
|
||||
export function getBindings<T extends GetBindingType>(env: any, type: T): BindingMap<T>[] {
|
||||
const bindings: BindingMap<T>[] = [];
|
||||
if (!(type in bindingMatchers)) {
|
||||
console.error(`No binding matcher for type ${type}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const matcher = bindingMatchers[type];
|
||||
|
||||
for (const key in env) {
|
||||
try {
|
||||
if (
|
||||
(env[key] as any).constructor.name === type ||
|
||||
String(env[key]) === `[object ${type}]` ||
|
||||
inspect(env[key]).includes(type)
|
||||
) {
|
||||
bindings.push({
|
||||
key,
|
||||
value: env[key] as BindingTypeMap[T],
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
const value = env[key];
|
||||
if (typeof value !== "object" || value === null) continue;
|
||||
if (!matcher(value)) continue;
|
||||
|
||||
bindings.push({
|
||||
key,
|
||||
value,
|
||||
} as any);
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, afterAll, test, expect, beforeAll } from "vitest";
|
||||
import { Miniflare } from "miniflare";
|
||||
import { getBindings, type GetBindingType } from "./bindings";
|
||||
|
||||
let mf: Miniflare | undefined;
|
||||
|
||||
beforeAll(() => {
|
||||
mf = new Miniflare({
|
||||
modules: true,
|
||||
script: `import { DurableObject } from "cloudflare:workers";
|
||||
export default { async fetch() { return new Response(null); } }
|
||||
export class TestObject extends DurableObject {}
|
||||
export class TestObject2 extends DurableObject {}`,
|
||||
r2Buckets: ["BUCKET"],
|
||||
d1Databases: ["DB"],
|
||||
kvNamespaces: ["KV"],
|
||||
durableObjects: {
|
||||
DO_SQLITE: { className: "TestObject", useSQLite: true },
|
||||
DO_KV: { className: "TestObject2" },
|
||||
},
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
mf?.dispose();
|
||||
});
|
||||
|
||||
describe("bindings", () => {
|
||||
test("extracts", async () => {
|
||||
const env = {
|
||||
...(await mf!.getBindings()),
|
||||
some: "string",
|
||||
another: 123,
|
||||
array: [1, 2, 3],
|
||||
};
|
||||
|
||||
const $getBindingKeys = (type: GetBindingType) => {
|
||||
const bindings = getBindings(env, type);
|
||||
const keys = bindings?.map((b) => b.key);
|
||||
if (!keys || keys.length === 0) {
|
||||
console.error(`No ${type} binding found`, { bindings });
|
||||
throw new Error(`No ${type} binding found`);
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
expect($getBindingKeys("D1Database")).toEqual(["DB"]);
|
||||
expect($getBindingKeys("KVNamespace")).toEqual(["KV"]);
|
||||
expect($getBindingKeys("DurableObjectNamespace")).toEqual(["DO_SQLITE", "DO_KV"]);
|
||||
expect($getBindingKeys("R2Bucket")).toEqual(["BUCKET"]);
|
||||
});
|
||||
});
|
||||
@@ -16,10 +16,14 @@ export {
|
||||
type GetBindingType,
|
||||
type BindingMap,
|
||||
} from "./bindings";
|
||||
export { constants, makeConfig, type CloudflareContext } from "./config";
|
||||
export {
|
||||
constants,
|
||||
makeConfig,
|
||||
type CloudflareContext,
|
||||
registerAsyncsExecutionContext,
|
||||
} from "./config";
|
||||
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
||||
export { registries } from "bknd";
|
||||
export { devFsVitePlugin, devFsWrite } from "./vite";
|
||||
|
||||
// for compatibility with old code
|
||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||
|
||||
@@ -16,6 +16,7 @@ export function registerMedia(
|
||||
env: Record<string, any>,
|
||||
registries: typeof $registries = $registries,
|
||||
) {
|
||||
if (registries.media.has("r2")) return;
|
||||
const r2_bindings = getBindings(env, "R2Bucket");
|
||||
|
||||
registries.media.register(
|
||||
@@ -172,7 +173,7 @@ export class StorageR2Adapter extends StorageAdapter {
|
||||
await this.bucket.delete(this.getKey(key));
|
||||
}
|
||||
|
||||
getObjectUrl(key: string): string {
|
||||
getObjectUrl(): string {
|
||||
throw new Error("Method getObjectUrl not implemented.");
|
||||
}
|
||||
|
||||
@@ -183,7 +184,7 @@ export class StorageR2Adapter extends StorageAdapter {
|
||||
return key;
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean) {
|
||||
toJSON() {
|
||||
return {
|
||||
type: this.getName(),
|
||||
config: {},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// biome-ignore-all lint: .
|
||||
import type { Plugin } from "vite";
|
||||
import { writeFile as nodeWriteFile } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
@@ -48,7 +49,6 @@ export function devFsVitePlugin({
|
||||
// Skip our own debug output
|
||||
if (output.includes("[dev-fs-plugin]") || output.includes("[dev-fs-polyfill]")) {
|
||||
// @ts-ignore
|
||||
// biome-ignore lint/style/noArguments: <explanation>
|
||||
return originalStdoutWrite.apply(process.stdout, arguments);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ export function devFsVitePlugin({
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
// biome-ignore lint:
|
||||
return originalStdoutWrite.apply(process.stdout, arguments);
|
||||
};
|
||||
|
||||
|
||||
+4
-1
@@ -73,8 +73,11 @@ export const env = <
|
||||
onValid?: (valid: R) => void;
|
||||
},
|
||||
): R extends undefined ? Fallback : R => {
|
||||
let source = opts?.source ?? {};
|
||||
try {
|
||||
source = process?.env ?? {};
|
||||
} catch (e) {}
|
||||
try {
|
||||
const source = opts?.source ?? process.env;
|
||||
const c = envs[key];
|
||||
const g = source[c.key];
|
||||
const v = c.validate(g) as any;
|
||||
|
||||
Reference in New Issue
Block a user