diff --git a/src/drivers/capacitor-preferences.ts b/src/drivers/capacitor-preferences.ts index aade7a8c..1a0a8015 100644 --- a/src/drivers/capacitor-preferences.ts +++ b/src/drivers/capacitor-preferences.ts @@ -8,46 +8,44 @@ export interface CapacitorPreferencesOptions { base?: string; } -export default defineDriver( - (opts) => { - const base = normalizeKey(opts?.base || ""); - const resolveKey = (key: string) => joinKeys(base, key); +export default defineDriver((opts: CapacitorPreferencesOptions) => { + const base = normalizeKey(opts?.base || ""); + const resolveKey = (key: string) => joinKeys(base, key); - return { - name: DRIVER_NAME, - options: opts, - getInstance: () => Preferences, - hasItem(key) { - return Preferences.keys().then((r) => r.keys.includes(resolveKey(key))); - }, - getItem(key) { - return Preferences.get({ key: resolveKey(key) }).then((r) => r.value); - }, - getItemRaw(key) { - return Preferences.get({ key: resolveKey(key) }).then((r) => r.value); - }, - setItem(key, value) { - return Preferences.set({ key: resolveKey(key), value }); - }, - setItemRaw(key, value) { - return Preferences.set({ key: resolveKey(key), value }); - }, - removeItem(key) { - return Preferences.remove({ key: resolveKey(key) }); - }, - async getKeys() { - const { keys } = await Preferences.keys(); - return keys.map((key) => key.slice(base.length)); - }, - async clear(prefix) { - const { keys } = await Preferences.keys(); - const _prefix = resolveKey(prefix || ""); - await Promise.all( - keys - .filter((key) => key.startsWith(_prefix)) - .map((key) => Preferences.remove({ key })) - ); - }, - }; - } -); + return { + name: DRIVER_NAME, + options: opts, + getInstance: () => Preferences, + hasItem(key) { + return Preferences.keys().then((r) => r.keys.includes(resolveKey(key))); + }, + getItem(key) { + return Preferences.get({ key: resolveKey(key) }).then((r) => r.value); + }, + getItemRaw(key) { + return Preferences.get({ key: resolveKey(key) }).then((r) => r.value); + }, + setItem(key, value) { + return Preferences.set({ key: resolveKey(key), value }); + }, + setItemRaw(key, value) { + return Preferences.set({ key: resolveKey(key), value }); + }, + removeItem(key) { + return Preferences.remove({ key: resolveKey(key) }); + }, + async getKeys() { + const { keys } = await Preferences.keys(); + return keys.map((key) => key.slice(base.length)); + }, + async clear(prefix) { + const { keys } = await Preferences.keys(); + const _prefix = resolveKey(prefix || ""); + await Promise.all( + keys + .filter((key) => key.startsWith(_prefix)) + .map((key) => Preferences.remove({ key })) + ); + }, + }; +}); diff --git a/src/drivers/cloudflare-kv-http.ts b/src/drivers/cloudflare-kv-http.ts index 049e22c2..c6e6d667 100644 --- a/src/drivers/cloudflare-kv-http.ts +++ b/src/drivers/cloudflare-kv-http.ts @@ -86,7 +86,7 @@ type CloudflareAuthorizationHeaders = const DRIVER_NAME = "cloudflare-kv-http"; -export default defineDriver((opts) => { +export default defineDriver((opts: KVHTTPOptions) => { if (!opts.accountId) { throw createRequiredError(DRIVER_NAME, "accountId"); } diff --git a/src/drivers/deno-kv-node.ts b/src/drivers/deno-kv-node.ts index 5c4d63fd..d03f8280 100644 --- a/src/drivers/deno-kv-node.ts +++ b/src/drivers/deno-kv-node.ts @@ -12,18 +12,16 @@ export interface DenoKvNodeOptions { const DRIVER_NAME = "deno-kv-node"; -export default defineDriver>( - (opts: DenoKvNodeOptions = {}) => { - const baseDriver = denoKV({ - ...opts, - openKv: () => openKv(opts.path, opts.openKvOptions), - }); - return { - ...baseDriver, - getInstance() { - return baseDriver.getInstance!() as Promise; - }, - name: DRIVER_NAME, - }; - } -); +export default defineDriver((opts: DenoKvNodeOptions = {}) => { + const baseDriver = denoKV({ + ...opts, + openKv: () => openKv(opts.path, opts.openKvOptions), + }); + return { + ...baseDriver, + getInstance() { + return baseDriver.getInstance!() as Promise; + }, + name: DRIVER_NAME, + }; +}); diff --git a/src/drivers/deno-kv.ts b/src/drivers/deno-kv.ts index 7830a1f5..571e807c 100644 --- a/src/drivers/deno-kv.ts +++ b/src/drivers/deno-kv.ts @@ -12,100 +12,96 @@ export interface DenoKvOptions { const DRIVER_NAME = "deno-kv"; -export default defineDriver>( - (opts: DenoKvOptions = {}) => { - const basePrefix: KvKey = opts.base - ? normalizeKey(opts.base).split(":") - : []; +export default defineDriver((opts: DenoKvOptions = {}) => { + const basePrefix: KvKey = opts.base ? normalizeKey(opts.base).split(":") : []; - const r = (key: string = ""): KvKey => - [...basePrefix, ...key.split(":")].filter(Boolean); + const r = (key: string = ""): KvKey => + [...basePrefix, ...key.split(":")].filter(Boolean); - let _kv: Promise | undefined; - const getKv = () => { - if (_kv) { - return _kv; + let _kv: Promise | undefined; + const getKv = () => { + if (_kv) { + return _kv; + } + if (opts.openKv) { + _kv = opts.openKv(); + } else { + if (!globalThis.Deno) { + throw createError( + DRIVER_NAME, + "Missing global `Deno`. Are you running in Deno? (hint: use `deno-kv-node` driver for Node.js)" + ); } - if (opts.openKv) { - _kv = opts.openKv(); - } else { - if (!globalThis.Deno) { - throw createError( - DRIVER_NAME, - "Missing global `Deno`. Are you running in Deno? (hint: use `deno-kv-node` driver for Node.js)" - ); - } - if (!Deno.openKv) { - throw createError( - DRIVER_NAME, - "Missing `Deno.openKv`. Are you running Deno with --unstable-kv?" - ); - } - _kv = Deno.openKv(opts.path); + if (!Deno.openKv) { + throw createError( + DRIVER_NAME, + "Missing `Deno.openKv`. Are you running Deno with --unstable-kv?" + ); } - return _kv; - }; + _kv = Deno.openKv(opts.path); + } + return _kv; + }; - return { - name: DRIVER_NAME, - getInstance() { - return getKv(); - }, - async hasItem(key) { - const kv = await getKv(); - const value = await kv.get(r(key)); - return !!value.value; - }, - async getItem(key) { - const kv = await getKv(); - const value = await kv.get(r(key)); - return value.value; - }, - async getItemRaw(key) { - const kv = await getKv(); - const value = await kv.get(r(key)); - return value.value; - }, - async setItem(key, value) { - const kv = await getKv(); - await kv.set(r(key), value); - }, - async setItemRaw(key, value) { - const kv = await getKv(); - await kv.set(r(key), value); - }, - async removeItem(key) { - const kv = await getKv(); - await kv.delete(r(key)); - }, - async getKeys(base) { - const kv = await getKv(); - const keys: string[] = []; - for await (const entry of kv.list({ prefix: r(base) })) { - keys.push( - (basePrefix.length > 0 - ? entry.key.slice(basePrefix.length) - : entry.key - ).join(":") - ); - } - return keys; - }, - async clear(base) { - const kv = await getKv(); - const batch = kv.atomic(); - for await (const entry of kv.list({ prefix: r(base) })) { - batch.delete(entry.key as KvKey); - } - await batch.commit(); - }, - async dispose() { - if (_kv) { - const kv = await _kv; - await kv.close(); - _kv = undefined; - } - }, - }; - } -); + return { + name: DRIVER_NAME, + getInstance() { + return getKv(); + }, + async hasItem(key) { + const kv = await getKv(); + const value = await kv.get(r(key)); + return !!value.value; + }, + async getItem(key) { + const kv = await getKv(); + const value = await kv.get(r(key)); + return value.value; + }, + async getItemRaw(key) { + const kv = await getKv(); + const value = await kv.get(r(key)); + return value.value; + }, + async setItem(key, value) { + const kv = await getKv(); + await kv.set(r(key), value); + }, + async setItemRaw(key, value) { + const kv = await getKv(); + await kv.set(r(key), value); + }, + async removeItem(key) { + const kv = await getKv(); + await kv.delete(r(key)); + }, + async getKeys(base) { + const kv = await getKv(); + const keys: string[] = []; + for await (const entry of kv.list({ prefix: r(base) })) { + keys.push( + (basePrefix.length > 0 + ? entry.key.slice(basePrefix.length) + : entry.key + ).join(":") + ); + } + return keys; + }, + async clear(base) { + const kv = await getKv(); + const batch = kv.atomic(); + for await (const entry of kv.list({ prefix: r(base) })) { + batch.delete(entry.key as KvKey); + } + await batch.commit(); + }, + async dispose() { + if (_kv) { + const kv = await _kv; + await kv.close(); + _kv = undefined; + } + }, + }; +}); diff --git a/src/drivers/github.ts b/src/drivers/github.ts index b886afa8..a6068eef 100644 --- a/src/drivers/github.ts +++ b/src/drivers/github.ts @@ -55,7 +55,7 @@ const defaultOptions: GithubOptions = { const DRIVER_NAME = "github"; -export default defineDriver((_opts) => { +export default defineDriver((_opts: GithubOptions) => { const opts: GithubOptions = { ...defaultOptions, ..._opts }; const rawUrl = joinURL(opts.cdnURL!, opts.repo, opts.branch!, opts.dir!); diff --git a/src/drivers/memory.ts b/src/drivers/memory.ts index 3f5601d0..3bea9739 100644 --- a/src/drivers/memory.ts +++ b/src/drivers/memory.ts @@ -2,7 +2,7 @@ import { defineDriver } from "./utils"; const DRIVER_NAME = "memory"; -export default defineDriver>(() => { +export default defineDriver(() => { const data = new Map(); return { diff --git a/src/drivers/null.ts b/src/drivers/null.ts index 33bef61e..dbd2c13e 100644 --- a/src/drivers/null.ts +++ b/src/drivers/null.ts @@ -2,7 +2,7 @@ import { defineDriver } from "./utils"; const DRIVER_NAME = "null"; -export default defineDriver(() => { +export default defineDriver(() => { return { name: DRIVER_NAME, hasItem() { diff --git a/src/drivers/session-storage.ts b/src/drivers/session-storage.ts index ab599e45..3c08a294 100644 --- a/src/drivers/session-storage.ts +++ b/src/drivers/session-storage.ts @@ -7,10 +7,10 @@ const DRIVER_NAME = "session-storage"; export default defineDriver((opts: SessionStorageOptions = {}) => { return { - name: DRIVER_NAME, ...localstorage({ windowKey: "sessionStorage", ...opts, }), + name: DRIVER_NAME, }; }); diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 8214ab91..d4bf2bbd 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -17,7 +17,7 @@ export interface UploadThingOptions extends UTApiOptions { const DRIVER_NAME = "uploadthing"; -export default defineDriver((opts = {}) => { +export default defineDriver((opts: UploadThingOptions = {}) => { let client: UTApi; const base = opts.base ? normalizeKey(opts.base) : ""; diff --git a/src/drivers/upstash.ts b/src/drivers/upstash.ts index dfac5cea..ba47dc02 100644 --- a/src/drivers/upstash.ts +++ b/src/drivers/upstash.ts @@ -15,59 +15,56 @@ export interface UpstashOptions extends Partial { const DRIVER_NAME = "upstash"; -export default defineDriver( - (options: UpstashOptions = {}) => { - const base = normalizeKey(options?.base); - const r = (...keys: string[]) => joinKeys(base, ...keys); +export default defineDriver((options: UpstashOptions = {}) => { + const base = normalizeKey(options?.base); + const r = (...keys: string[]) => joinKeys(base, ...keys); - let redisClient: Redis; - const getClient = () => { - if (redisClient) { - return redisClient; - } - const url = - options.url || globalThis.process?.env?.UPSTASH_REDIS_REST_URL; - const token = - options.token || globalThis.process?.env?.UPSTASH_REDIS_REST_TOKEN; - redisClient = new Redis({ url, token, ...options }); + let redisClient: Redis; + const getClient = () => { + if (redisClient) { return redisClient; - }; - return { - name: DRIVER_NAME, - getInstance: getClient, - hasItem(key) { - return getClient().exists(r(key)).then(Boolean); - }, - getItem(key) { - return getClient().get(r(key)); - }, - setItem(key, value, tOptions) { - const ttl = tOptions?.ttl || options.ttl; - return getClient() - .set(r(key), value, ttl ? { ex: ttl } : undefined) - .then(() => {}); - }, - removeItem(key) { - return getClient() - .del(r(key)) - .then(() => {}); - }, - getKeys(_base) { - return getClient() - .keys(r(_base, "*")) - .then((keys) => - base ? keys.map((key) => key.slice(base.length + 1)) : keys - ); - }, - async clear(base) { - const keys = await getClient().keys(r(base, "*")); - if (keys.length === 0) { - return; - } - return getClient() - .del(...keys) - .then(() => {}); - }, - }; - } -); + } + const url = options.url || globalThis.process?.env?.UPSTASH_REDIS_REST_URL; + const token = + options.token || globalThis.process?.env?.UPSTASH_REDIS_REST_TOKEN; + redisClient = new Redis({ url, token, ...options }); + return redisClient; + }; + return { + name: DRIVER_NAME, + getInstance: getClient, + hasItem(key) { + return getClient().exists(r(key)).then(Boolean); + }, + getItem(key) { + return getClient().get(r(key)); + }, + setItem(key, value, tOptions) { + const ttl = tOptions?.ttl || options.ttl; + return getClient() + .set(r(key), value, ttl ? { ex: ttl } : undefined) + .then(() => {}); + }, + removeItem(key) { + return getClient() + .del(r(key)) + .then(() => {}); + }, + getKeys(_base) { + return getClient() + .keys(r(_base, "*")) + .then((keys) => + base ? keys.map((key) => key.slice(base.length + 1)) : keys + ); + }, + async clear(base) { + const keys = await getClient().keys(r(base, "*")); + if (keys.length === 0) { + return; + } + return getClient() + .del(...keys) + .then(() => {}); + }, + }; +}); diff --git a/src/drivers/utils/index.ts b/src/drivers/utils/index.ts index 16e584a3..8d2d2363 100644 --- a/src/drivers/utils/index.ts +++ b/src/drivers/utils/index.ts @@ -1,13 +1,15 @@ import type { Driver } from "../.."; -type DriverFactory = ( - opts: OptionsT -) => Driver; interface ErrorOptions {} -export function defineDriver( - factory: DriverFactory -): DriverFactory { +export function defineDriver< + TOptions, + TInstance, + TDriver extends Driver, + TArgs extends unknown[], +>( + factory: (...args: TArgs) => TDriver +): (...args: TArgs) => TDriver & Driver { return factory; } diff --git a/src/drivers/vercel-blob.ts b/src/drivers/vercel-blob.ts index e67a787d..6440266d 100644 --- a/src/drivers/vercel-blob.ts +++ b/src/drivers/vercel-blob.ts @@ -27,7 +27,7 @@ export interface VercelBlobOptions { const DRIVER_NAME = "vercel-blob"; -export default defineDriver((opts) => { +export default defineDriver((opts: VercelBlobOptions) => { const optsBase = normalizeKey(opts?.base); const r = (...keys: string[]) => diff --git a/src/drivers/vercel-kv.ts b/src/drivers/vercel-kv.ts index c151e5b7..96b0b593 100644 --- a/src/drivers/vercel-kv.ts +++ b/src/drivers/vercel-kv.ts @@ -23,7 +23,7 @@ export interface VercelKVOptions extends Partial { const DRIVER_NAME = "vercel-kv"; -export default defineDriver((opts) => { +export default defineDriver((opts: VercelKVOptions) => { const base = normalizeKey(opts?.base); const r = (...keys: string[]) => joinKeys(base, ...keys); diff --git a/src/types.ts b/src/types.ts index 05062844..a273bce3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,7 +26,7 @@ export interface Driver { name?: string; options?: OptionsT; getInstance?: () => InstanceT; - hasItem: (key: string, opts: TransactionOptions) => MaybePromise; + hasItem: (key: string, opts?: TransactionOptions) => MaybePromise; getItem: ( key: string, opts?: TransactionOptions @@ -37,11 +37,14 @@ export interface Driver { commonOptions?: TransactionOptions ) => MaybePromise<{ key: string; value: StorageValue }[]>; /** @experimental */ - getItemRaw?: (key: string, opts: TransactionOptions) => MaybePromise; + getItemRaw?: ( + key: string, + opts?: TransactionOptions + ) => MaybePromise; setItem?: ( key: string, value: string, - opts: TransactionOptions + opts?: TransactionOptions ) => MaybePromise; /** @experimental */ setItems?: ( @@ -52,15 +55,15 @@ export interface Driver { setItemRaw?: ( key: string, value: any, - opts: TransactionOptions + opts?: TransactionOptions ) => MaybePromise; removeItem?: (key: string, opts: TransactionOptions) => MaybePromise; getMeta?: ( key: string, - opts: TransactionOptions + opts?: TransactionOptions ) => MaybePromise; - getKeys: (base: string, opts: GetKeysOptions) => MaybePromise; - clear?: (base: string, opts: TransactionOptions) => MaybePromise; + getKeys: (base: string, opts?: GetKeysOptions) => MaybePromise; + clear?: (base: string, opts?: TransactionOptions) => MaybePromise; dispose?: () => MaybePromise; watch?: (callback: WatchCallback) => MaybePromise; } diff --git a/test/storage.test.ts b/test/storage.test.ts index 498befdb..ee9abc90 100644 --- a/test/storage.test.ts +++ b/test/storage.test.ts @@ -4,6 +4,7 @@ import { snapshot, restoreSnapshot, prefixStorage, + type Driver, } from "../src"; import memory from "../src/drivers/memory"; @@ -184,7 +185,7 @@ describe("Regression", () => { const setItem = vi.fn(); const setItems = vi.fn(); - const driver = memory(); + const driver = memory() as Driver; const storage = createStorage({ driver: { ...driver,