Skip to content

Commit

Permalink
refactor(cloudflare-kv, cloudflare-r2): move getBindings to utils a…
Browse files Browse the repository at this point in the history
…nd add default `BUCKET` for r2 (#292)

Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
oritwoen and pi0 authored Jan 12, 2024
1 parent 78e1568 commit f7e7007
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 79 deletions.
11 changes: 8 additions & 3 deletions docs/content/6.drivers/cloudflare-r2-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@ import cloudflareR2BindingDriver from "unstorage/drivers/cloudflare-r2-binding";

// Using binding name to be picked from globalThis
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: "MY_BUCKET" }),
driver: cloudflareR2BindingDriver({ binding: "BUCKET" }),
});

// Directly setting binding
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: globalThis.MY_BUCKET }),
driver: cloudflareR2BindingDriver({ binding: globalThis.BUCKET }),
});

// Using from Durable Objects and Workers using Modules Syntax
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: this.env.BUCKET }),
});
```

**Options:**

- `binding`: Bucket binding or name.
- `binding`: Bucket binding or name. Default is `BUCKET`.
- `base`: Prefix all keys with base.
45 changes: 9 additions & 36 deletions src/drivers/cloudflare-kv-binding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="@cloudflare/workers-types" />
import { createError, defineDriver, joinKeys } from "./utils";
import { defineDriver, joinKeys } from "./utils";
import { getKVBinding } from "./utils/cloudflare";
export interface KVOptions {
binding?: string | KVNamespace;

Expand All @@ -11,12 +12,12 @@ export interface KVOptions {

const DRIVER_NAME = "cloudflare-kv-binding";

export default defineDriver((opts: KVOptions = {}) => {
export default defineDriver((opts: KVOptions) => {
const r = (key: string = "") => (opts.base ? joinKeys(opts.base, key) : key);

async function getKeys(base: string = "") {
base = r(base);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
const kvList = await binding.list(base ? { prefix: base } : undefined);
return kvList.keys.map((key) => key.name);
}
Expand All @@ -26,22 +27,22 @@ export default defineDriver((opts: KVOptions = {}) => {
options: opts,
async hasItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return (await binding.get(key)) !== null;
},
getItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.get(key);
},
setItem(key, value) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.put(key, value);
},
removeItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.delete(key);
},
getKeys() {
Expand All @@ -50,37 +51,9 @@ export default defineDriver((opts: KVOptions = {}) => {
);
},
async clear(base) {
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
const keys = await getKeys(base);
await Promise.all(keys.map((key) => binding.delete(key)));
},
};
});

function getBinding(binding: KVNamespace | string = "STORAGE") {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as KVNamespace;
}

if (!binding) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}
}

return binding;
}
53 changes: 13 additions & 40 deletions src/drivers/cloudflare-r2-binding.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
/// <reference types="@cloudflare/workers-types" />
import { createError, defineDriver, joinKeys } from "./utils";
import { defineDriver, joinKeys } from "./utils";
import { getR2Binding } from "./utils/cloudflare";

export interface CloudflareR2Options {
binding: string | R2Bucket;
binding?: string | R2Bucket;
base?: string;
}

// https://developers.cloudflare.com/r2/api/workers/workers-api-reference/

const DRIVER_NAME = "cloudflare-r2-binding";

export default defineDriver((opts: CloudflareR2Options) => {
export default defineDriver((opts: CloudflareR2Options = {}) => {
const r = (key: string = "") => (opts.base ? joinKeys(opts.base, key) : key);

const getKeys = async (base?: string) => {
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
const kvList = await binding.list(
base || opts.base ? { prefix: r(base) } : undefined
);
Expand All @@ -26,12 +27,12 @@ export default defineDriver((opts: CloudflareR2Options) => {
options: opts,
async hasItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return (await binding.head(key)) !== null;
},
async getMeta(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
const obj = await binding.head(key);
if (!obj) return null;
return {
Expand All @@ -42,27 +43,27 @@ export default defineDriver((opts: CloudflareR2Options) => {
},
getItem(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return binding.get(key, topts).then((r) => r?.text());
},
getItemRaw(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return binding.get(key, topts).then((r) => r?.arrayBuffer());
},
async setItem(key, value, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.put(key, value, topts);
},
async setItemRaw(key, value, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.put(key, value, topts);
},
async removeItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.delete(key);
},
getKeys(base) {
Expand All @@ -71,37 +72,9 @@ export default defineDriver((opts: CloudflareR2Options) => {
);
},
async clear(base) {
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
const keys = await getKeys(base);
await binding.delete(keys);
},
};
});

function getBinding(binding: R2Bucket | string) {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as R2Bucket;
}

if (!binding) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}
}

return binding;
}
38 changes: 38 additions & 0 deletions src/drivers/utils/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/// <reference types="@cloudflare/workers-types" />
import { createError } from "./index";

export function getBinding(binding: KVNamespace | R2Bucket | string) {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as KVNamespace | R2Bucket;
}

if (!binding) {
throw createError(
"cloudflare",
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
"cloudflare",
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}
}

return binding;
}

export function getKVBinding(binding: KVNamespace | string = "STORAGE") {
return getBinding(binding) as KVNamespace;
}

export function getR2Binding(binding: R2Bucket | string = "BUCKET") {
return getBinding(binding) as R2Bucket;
}

0 comments on commit f7e7007

Please sign in to comment.