Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/happy-pandas-sparkle.md
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion fixtures/get-bindings-proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"devDependencies": {
"@cloudflare/workers-tsconfig": "workspace:*",
"@cloudflare/workers-types": "^4.20221111.1",
"wrangler": "workspace:*"
"wrangler": "workspace:*",
"undici": "^5.28.2"
}
}
34 changes: 34 additions & 0 deletions fixtures/get-bindings-proxy/tests/get-bindings-proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Bindings>({
configPath: wranglerTomlFilePath,
});
try {
const cache =
cacheType === "default"
? caches.default
: await caches.open("my-cache");
testNoOpCache(cache);
} finally {
await dispose();
}
})
);
});
});

/**
Expand Down Expand Up @@ -263,3 +283,17 @@ async function testDoBinding(
const doRespText = await doResp.text();
expect(doRespText).toBe(expectedResponse);
}

async function testNoOpCache(
cache: Awaited<ReturnType<typeof getBindingsProxy>>["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);
}
62 changes: 62 additions & 0 deletions packages/wrangler/src/api/integrations/bindings/caches.ts
Original file line number Diff line number Diff line change
@@ -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<Cache> {
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<boolean> {
return false;
}

async match(
request: CacheRequest,
options?: CacheQueryOptions
): Promise<CacheResponse | undefined> {
return undefined;
}

async put(request: CacheRequest, response: CacheResponse): Promise<void> {}
}

type CacheQueryOptions = {
ignoreMethod?: boolean;
};
6 changes: 6 additions & 0 deletions packages/wrangler/src/api/integrations/bindings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -39,6 +40,10 @@ export type BindingsProxy<Bindings = Record<string, unknown>> = {
* 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
*/
Expand Down Expand Up @@ -83,6 +88,7 @@ export async function getBindingsProxy<Bindings = Record<string, unknown>>(
...vars,
...bindings,
},
caches: new CacheStorage(),
dispose: () => mf.dispose(),
};
}
Expand Down
15 changes: 9 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.