/** * Configure OpenAuth to use [Cloudflare KV](https://developers.cloudflare.com/kv/) as a * storage adapter. * * ```ts * import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" * * const storage = CloudflareStorage({ * namespace: "my-namespace" * }) * * * export default issuer({ * storage, * // ... * }) * ``` * * @packageDocumentation */ import type { KVNamespace } from "@cloudflare/workers-types" import { joinKey, splitKey, StorageAdapter } from "./storage.js" /** * Configure the Cloudflare KV store that's created. */ export interface CloudflareStorageOptions { namespace: KVNamespace } /** * Creates a Cloudflare KV store. * @param options - The config for the adapter. */ export function CloudflareStorage( options: CloudflareStorageOptions, ): StorageAdapter { return { async get(key: string[]) { const value = await options.namespace.get(joinKey(key), "json") if (!value) return return value as Record }, async set(key: string[], value: any, expiry?: Date) { await options.namespace.put(joinKey(key), JSON.stringify(value), { expirationTtl: expiry ? Math.floor((expiry.getTime() - Date.now()) / 1000) : undefined, }) }, async remove(key: string[]) { await options.namespace.delete(joinKey(key)) }, async *scan(prefix: string[]) { let cursor: string | undefined while (true) { const result = await options.namespace.list({ prefix: joinKey([...prefix, ""]), cursor, }) for (const key of result.keys) { const value = await options.namespace.get(key.name, "json") if (value !== null) { yield [splitKey(key.name), value] } } if (result.list_complete) { break } cursor = result.cursor } }, } }