diff --git a/.changeset/happy-pandas-sparkle.md b/.changeset/happy-pandas-sparkle.md new file mode 100644 index 000000000000..3a9417ace9b3 --- /dev/null +++ b/.changeset/happy-pandas-sparkle.md @@ -0,0 +1,12 @@ +--- +"wrangler": minor +--- + +feat: expose new (no-op) `caches` field in `getBindingsProxy` result + +Add a new `caches` field to the `getBindingsProxy` result, such field implements a +no operation (no-op) implementation of the runtime `caches` + +Note: Miniflare exposes a proper `caches` mock, we will want to use that one in +the future but issues regarding it must be ironed out first, so for the +time being a no-op will have to do diff --git a/fixtures/get-bindings-proxy/package.json b/fixtures/get-bindings-proxy/package.json index dd5f5a695774..838544b43098 100644 --- a/fixtures/get-bindings-proxy/package.json +++ b/fixtures/get-bindings-proxy/package.json @@ -10,6 +10,7 @@ "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", "@cloudflare/workers-types": "^4.20221111.1", - "wrangler": "workspace:*" + "wrangler": "workspace:*", + "undici": "^5.28.2" } } diff --git a/fixtures/get-bindings-proxy/tests/get-bindings-proxy.test.ts b/fixtures/get-bindings-proxy/tests/get-bindings-proxy.test.ts index a14f7c6ded39..560840f2323d 100644 --- a/fixtures/get-bindings-proxy/tests/get-bindings-proxy.test.ts +++ b/fixtures/get-bindings-proxy/tests/get-bindings-proxy.test.ts @@ -6,6 +6,7 @@ import { Fetcher, R2Bucket, } from "@cloudflare/workers-types"; +import { Request, Response } from "undici"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { getBindingsProxy as originalGetBindingsProxy, @@ -227,6 +228,25 @@ describe("getBindingsProxy", () => { await dispose(); } }); + + describe("caches", () => { + (["default", "named"] as const).forEach((cacheType) => + it(`correctly obtains a no-op ${cacheType} cache`, async () => { + const { caches, dispose } = await getBindingsProxy({ + configPath: wranglerTomlFilePath, + }); + try { + const cache = + cacheType === "default" + ? caches.default + : await caches.open("my-cache"); + testNoOpCache(cache); + } finally { + await dispose(); + } + }) + ); + }); }); /** @@ -263,3 +283,17 @@ async function testDoBinding( const doRespText = await doResp.text(); expect(doRespText).toBe(expectedResponse); } + +async function testNoOpCache( + cache: Awaited>["caches"]["default"] +) { + let match = await cache.match("http://0.0.0.0/test"); + expect(match).toBeUndefined(); + + const req = new Request("http://0.0.0.0/test"); + await cache.put(req, new Response("test")); + const resp = await cache.match(req); + expect(resp).toBeUndefined(); + const deleted = await cache.delete(req); + expect(deleted).toBe(false); +} diff --git a/packages/wrangler/src/api/integrations/bindings/caches.ts b/packages/wrangler/src/api/integrations/bindings/caches.ts new file mode 100644 index 000000000000..b6a5858285c0 --- /dev/null +++ b/packages/wrangler/src/api/integrations/bindings/caches.ts @@ -0,0 +1,62 @@ +/* eslint-disable unused-imports/no-unused-vars */ + +/** + * Note about this file: + * + * Here we are providing a no-op implementation of the runtime Cache API instead of using + * the miniflare implementation (via `mf.getCaches()`). + * + * We are not using miniflare's implementation because that would require the user to provide + * miniflare-specific Request objects and they would receive back miniflare-specific Response + * objects, this (in particular the Request part) is not really suitable for `getBindingsProxy` + * as people would ideally interact with their bindings in a very production-like manner and + * requiring them to deal with miniflare-specific classes defeats a bit the purpose of the utility. + * + * Similarly the Request and Response types here are set to `undefined` as not to use specific ones + * that would require us to make a choice right now or the user to adapt their code in order to work + * with the api. + * + * We need to find a better/generic manner in which we can reuse the miniflare cache implementation, + * but until then the no-op implementation below will have to do. + */ + +/** + * No-op implementation of CacheStorage + */ +export class CacheStorage { + async open(cacheName: string): Promise { + return new Cache(); + } + + get default(): Cache { + return new Cache(); + } +} + +type CacheRequest = unknown; +type CacheResponse = unknown; + +/** + * No-op implementation of Cache + */ +class Cache { + async delete( + request: CacheRequest, + options?: CacheQueryOptions + ): Promise { + return false; + } + + async match( + request: CacheRequest, + options?: CacheQueryOptions + ): Promise { + return undefined; + } + + async put(request: CacheRequest, response: CacheResponse): Promise {} +} + +type CacheQueryOptions = { + ignoreMethod?: boolean; +}; diff --git a/packages/wrangler/src/api/integrations/bindings/index.ts b/packages/wrangler/src/api/integrations/bindings/index.ts index 1720cc4a36da..dcb510321253 100644 --- a/packages/wrangler/src/api/integrations/bindings/index.ts +++ b/packages/wrangler/src/api/integrations/bindings/index.ts @@ -4,6 +4,7 @@ import { getBindings } from "../../../dev"; import { getBoundRegisteredWorkers } from "../../../dev-registry"; import { getVarsForDev } from "../../../dev/dev-vars"; import { buildMiniflareBindingOptions } from "../../../dev/miniflare"; +import { CacheStorage } from "./caches"; import { getServiceBindings } from "./services"; import type { Config } from "../../../config"; import type { MiniflareOptions } from "miniflare"; @@ -39,6 +40,10 @@ export type BindingsProxy> = { * Object containing the various proxies */ bindings: Bindings; + /** + * Caches object emulating the Workers Cache runtime API + */ + caches: CacheStorage; /** * Function used to dispose of the child process providing the bindings implementation */ @@ -83,6 +88,7 @@ export async function getBindingsProxy>( ...vars, ...bindings, }, + caches: new CacheStorage(), dispose: () => mf.dispose(), }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f626c0ade075..45c470089867 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,6 +209,9 @@ importers: '@cloudflare/workers-types': specifier: ^4.20221111.1 version: 4.20231025.0 + undici: + specifier: ^5.28.2 + version: 5.28.2 wrangler: specifier: workspace:* version: link:../../packages/wrangler @@ -802,7 +805,7 @@ importers: version: 8.49.0 eslint-config-turbo: specifier: latest - version: 1.10.13(eslint@8.49.0) + version: 1.10.15(eslint@8.49.0) eslint-plugin-import: specifier: 2.26.x version: 2.26.0(@typescript-eslint/parser@6.7.2)(eslint@8.49.0) @@ -10700,13 +10703,13 @@ packages: eslint: 8.49.0 dev: true - /eslint-config-turbo@1.10.13(eslint@8.49.0): - resolution: {integrity: sha512-Ffa0SxkRCPMtfUX/HDanEqsWoLwZTQTAXO9W4IsOtycb2MzJDrVcLmoFW5sMwCrg7gjqbrC4ZJoD+1SPPzIVqg==} + /eslint-config-turbo@1.10.15(eslint@8.49.0): + resolution: {integrity: sha512-76mpx2x818JZE26euen14utYcFDxOahZ9NaWA+6Xa4pY2ezVKVschuOxS96EQz3o3ZRSmcgBOapw/gHbN+EKxQ==} peerDependencies: eslint: '>6.6.0' dependencies: eslint: 8.49.0 - eslint-plugin-turbo: 1.10.13(eslint@8.49.0) + eslint-plugin-turbo: 1.10.15(eslint@8.49.0) dev: false /eslint-import-resolver-node@0.3.7: @@ -11133,8 +11136,8 @@ packages: - typescript dev: true - /eslint-plugin-turbo@1.10.13(eslint@8.49.0): - resolution: {integrity: sha512-el4AAmn0zXmvHEyp1h0IQMfse10Vy8g5Vbg4IU3+vD9CSj5sDbX07iFVt8sCKg7og9Q5FAa9mXzlCf7t4vYgzg==} + /eslint-plugin-turbo@1.10.15(eslint@8.49.0): + resolution: {integrity: sha512-Tv4QSKV/U56qGcTqS/UgOvb9HcKFmWOQcVh3HEaj7of94lfaENgfrtK48E2CckQf7amhKs1i+imhCsNCKjkQyA==} peerDependencies: eslint: '>6.6.0' dependencies: