Last active
January 6, 2025 13:55
-
-
Save andyjessop/cec5a71d885d52fab54e5b4d43fb264f to your computer and use it in GitHub Desktop.
PouchDB-CloudflareKV adapter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AbstractLevelDOWN } from "abstract-leveldown"; | |
import type { AsyncMap } from "../../../../packages/async-map/src/types.ts"; | |
type Callback<T = void> = (error?: Error | null, result?: T) => void; | |
/** | |
* A utility for enabling hook up between various "AsyncMap" storage mechanisms to PouchDB via the Level adapter | |
*/ | |
export class AsyncMapLevel extends AbstractLevelDOWN<string, string> { | |
#storage: AsyncMap<string, string>; | |
constructor(storage: AsyncMap<string, string>, location?: string) { | |
super(typeof location === "string" ? location : ""); | |
this.#storage = storage; | |
} | |
protected _serializeKey(key: string | Buffer): string { | |
// If you want pure pass-through, treat Buffers as UTF-8 or otherwise | |
// handle them. For plain strings, simply return the string itself. | |
return key.toString(); | |
} | |
protected _serializeValue(value: string | Buffer): string { | |
return value.toString(); | |
} | |
_info(callback: Callback<any>): void { | |
Promise.resolve().then(() => callback(null, { type: "async-map" })); | |
} | |
_open(_: any, callback: Callback<this>): void { | |
Promise.resolve().then(() => callback(null, this)); | |
} | |
_put(key: string, value: string, _: any, callback: Callback): void { | |
this.#storage.set(key, value).then(() => callback()); | |
} | |
_get(key: string, _: any, callback: Callback<string | Buffer>): void { | |
this.#storage.get(key).then((value) => { | |
if (value === undefined) { | |
callback(new Error("NotFound")); | |
} else { | |
callback(null, value); | |
} | |
}); | |
} | |
_del(key: string, _: any, callback: Callback): void { | |
this.#storage.delete(key).then(() => callback()); | |
} | |
_batch( | |
array: Array<{ key: string; value?: string; type: "put" | "del" }>, | |
_: any, | |
callback: Callback, | |
): void { | |
const promises = []; | |
for (const operation of array) { | |
if (!operation) continue; | |
const { key, value, type } = operation; | |
if (type === "put" && value !== undefined) { | |
promises.push(this.#storage.set(key, value)); | |
} else if (type === "del") { | |
promises.push(this.#storage.delete(key)); | |
} | |
} | |
Promise.all(promises).then(() => callback()); | |
} | |
static destroy(_: string, callback: Callback): void { | |
Promise.resolve().then(() => callback()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const kv = c.env.MY_KV; | |
PouchDB.adapter("kv", createKvPouch(new KvAsyncMap(kv)), true); | |
const kvDb = new PouchDB("my-db", { adapter: "kv" }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { AsyncMap } from "../../../../packages/async-map/src/types"; | |
export class KvAsyncMap<K, V> implements AsyncMap<K, V> { | |
private kvNamespace: KVNamespace; | |
constructor(kvNamespace: KVNamespace) { | |
this.kvNamespace = kvNamespace; | |
} | |
async clear(): Promise<void> { | |
const keys = await this.kvNamespace.list(); | |
for (const key of keys.keys) { | |
await this.kvNamespace.delete(key.name); | |
} | |
} | |
async delete(key: K): Promise<boolean> { | |
const keyString = String(key); // Ensure the key is a string (Cloudflare KV requires string keys) | |
const value = await this.kvNamespace.get(keyString); | |
if (value === null) { | |
return false; // Key doesn't exist | |
} | |
await this.kvNamespace.delete(keyString); | |
return true; | |
} | |
async forEach( | |
callbackFn: (value: V, key: K, map: AsyncMap<K, V>) => void | Promise<void>, | |
thisArg?: any, | |
): Promise<void> { | |
const keys = await this.kvNamespace.list(); | |
for (const key of keys.keys) { | |
const value = await this.kvNamespace.get(key.name); | |
if (value !== null) { | |
await callbackFn.call(thisArg, value as V, key.name as K, this); | |
} | |
} | |
} | |
async get(key: K): Promise<V | undefined> { | |
const keyString = String(key); // Ensure the key is a string | |
const value = await this.kvNamespace.get(keyString); | |
return value ? (value as V) : undefined; | |
} | |
async has(key: K): Promise<boolean> { | |
const keyString = String(key); // Ensure the key is a string | |
const value = await this.kvNamespace.get(keyString); | |
return value !== null; | |
} | |
async set(key: K, value: V): Promise<void> { | |
const keyString = String(key); // Ensure the key is a string | |
// @ts-ignore | |
await this.kvNamespace.put(keyString, value); | |
} | |
async size(): Promise<number> { | |
const keys = await this.kvNamespace.list(); | |
return keys.keys.length; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { AsyncMap } from "async-map/src/types.ts"; | |
import CoreLevelPouch from "pouchdb-adapter-leveldb-core"; | |
import { AsyncMapLevel } from "./AsyncMapLevel.ts"; | |
export function createKvPouch(stub: AsyncMap<string, string>) { | |
KVPouch.valid = () => true; | |
KVPouch.use_prefix = false; | |
function KVPouch(opts: any, callback: any) { | |
const _opts = Object.assign( | |
{ | |
db: (location: string) => { | |
return new AsyncMapLevel(stub, location); | |
}, | |
}, | |
opts, | |
); | |
CoreLevelPouch.call(this, _opts, callback); | |
} | |
return KVPouch; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment