From 8ff4604a7234cde78d7b73d7b98a824d7df8aa3b Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 12 Mar 2024 14:46:30 +0000 Subject: [PATCH] add bindingNames to getPlatformProxy result --- .../api/integrations/platform/bindingNames.ts | 121 ++++++++++++++++++ .../src/api/integrations/platform/index.ts | 35 +++-- .../wrangler/src/api/integrations/utils.ts | 10 ++ 3 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 packages/wrangler/src/api/integrations/platform/bindingNames.ts create mode 100644 packages/wrangler/src/api/integrations/utils.ts diff --git a/packages/wrangler/src/api/integrations/platform/bindingNames.ts b/packages/wrangler/src/api/integrations/platform/bindingNames.ts new file mode 100644 index 000000000000..03ed1a87f013 --- /dev/null +++ b/packages/wrangler/src/api/integrations/platform/bindingNames.ts @@ -0,0 +1,121 @@ +import { deepFreeze } from "../utils"; + +export type BindingNames = Record< + "var" | "service" | "kv" | "do" | "d1" | "r2" | "queue", + string[] +>; + +export function getBindingNames( + env: Record +): Readonly { + const names: BindingNames = { + var: [], + service: [], + kv: [], + do: [], + d1: [], + r2: [], + queue: [], + }; + + Object.entries(env).forEach(([name, binding]) => { + if (isVar(binding)) { + return names.var.push(name); + } + if (isService(binding)) { + return names.service.push(name); + } + if (isKv(binding)) { + return names.kv.push(name); + } + if (isDo(binding)) { + return names.do.push(name); + } + if (isR2(binding)) { + return names.r2.push(name); + } + if (isD1(binding)) { + return names.d1.push(name); + } + if (isQueue(binding)) { + return names.queue.push(name); + } + }); + + deepFreeze(names); + return names; +} + +function isVar(binding: unknown): boolean { + if (typeof binding === "string") { + return true; + } + + // TO IMPLEMENT + // if(isfullyserializable) return true; + + return false; +} + +function containsCrudLikeMethods(binding: Record): boolean { + return [binding.get, binding.put, binding.delete, binding.list].every( + (field) => typeof field === "function" + ); +} + +// NOTE: unfortunately the AI bindings is considered a service here, since +// it is implemented using a fetcher and so it is completely +// indistinguishable from a standard service binding +function isService(binding: unknown): boolean { + const asRecord = binding as Record; + + return [asRecord.fetch, asRecord.connect].every( + (field) => typeof field === "function" + ); +} + +function isKv(binding: unknown): boolean { + if (!containsCrudLikeMethods(binding as Record)) { + return false; + } + return !containsR2Methods(binding as Record); +} + +function containsR2Methods(binding: Record): boolean { + return [binding.createMultipartUpload, binding.resumeMultipartUpload].every( + (field) => typeof field === "function" + ); +} + +function isR2(binding: unknown): boolean { + if (!containsCrudLikeMethods(binding as Record)) { + return false; + } + return containsR2Methods(binding as Record); +} + +function isDo(binding: unknown): boolean { + const asRecord = binding as Record; + return [ + asRecord.get, + asRecord.idFromName, + asRecord.idFromString, + asRecord.newUniqueId, + ].every((field) => typeof field === "function"); +} + +function isD1(binding: unknown): boolean { + const asRecord = binding as Record; + + return [asRecord.batch, asRecord.dump, asRecord.exec, asRecord.prepare].every( + (field) => typeof field === "function" + ); +} + +function isQueue(binding: unknown): boolean { + const asRecord = binding as Record; + + return [asRecord.send, asRecord.sendBatch].every( + (field) => typeof field === "function" + ); +} diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts index ff885eb7b102..302fb97f2da8 100644 --- a/packages/wrangler/src/api/integrations/platform/index.ts +++ b/packages/wrangler/src/api/integrations/platform/index.ts @@ -8,10 +8,13 @@ import { buildSitesOptions, } from "../../../dev/miniflare"; import { getAssetPaths, getSiteAssetPaths } from "../../../sites"; +import { deepFreeze } from "../utils"; +import { getBindingNames } from "./bindingNames"; import { CacheStorage } from "./caches"; import { ExecutionContext } from "./executionContext"; import { getServiceBindings } from "./services"; import type { Config } from "../../../config"; +import type { BindingNames } from "./bindingNames"; import type { IncomingRequestCfProperties } from "@cloudflare/workers-types/experimental"; import type { MiniflareOptions, WorkerOptions } from "miniflare"; @@ -49,6 +52,10 @@ export type PlatformProxy< * Environment object containing the various Cloudflare bindings */ env: Env; + /** + * Name of the available bindings, grouped by type + */ + bindingNames: Readonly; /** * Mock of the context object that Workers received in their request handler, all the object's methods are no-op */ @@ -86,11 +93,11 @@ export async function getPlatformProxy< }); // getBindingsProxy doesn't currently support selecting an environment - const env = undefined; + const environment = undefined; const miniflareOptions = await getMiniflareOptionsFromConfig( rawConfig, - env, + environment, options ); @@ -102,16 +109,19 @@ export async function getPlatformProxy< const bindings: Env = await mf.getBindings(); - const vars = getVarsForDev(rawConfig, env); + const vars = getVarsForDev(rawConfig, environment); const cf = await mf.getCf(); deepFreeze(cf); + const env = { + ...vars, + ...bindings, + }; + return { - env: { - ...vars, - ...bindings, - }, + env, + bindingNames: getBindingNames(env), cf: cf as CfProperties, ctx: new ExecutionContext(), caches: new CacheStorage(), @@ -198,17 +208,6 @@ function getMiniflarePersistOptions( }; } -function deepFreeze>( - obj: T -): void { - Object.freeze(obj); - Object.entries(obj).forEach(([, prop]) => { - if (prop !== null && typeof prop === "object" && !Object.isFrozen(prop)) { - deepFreeze(prop as Record); - } - }); -} - export type SourcelessWorkerOptions = Omit< WorkerOptions, "script" | "scriptPath" | "modules" | "modulesRoot" | "modulesRule" diff --git a/packages/wrangler/src/api/integrations/utils.ts b/packages/wrangler/src/api/integrations/utils.ts new file mode 100644 index 000000000000..115197be289f --- /dev/null +++ b/packages/wrangler/src/api/integrations/utils.ts @@ -0,0 +1,10 @@ +export function deepFreeze>( + obj: T +): void { + Object.freeze(obj); + Object.entries(obj).forEach(([, prop]) => { + if (prop !== null && typeof prop === "object" && !Object.isFrozen(prop)) { + deepFreeze(prop as Record); + } + }); +}