Skip to content
Open
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
5 changes: 3 additions & 2 deletions src/drivers/deno-kv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface DenoKvOptions {
*/
ttl?: number;
}

interface DenoKVSetOptions {
/**
* TTL in seconds.
Expand Down Expand Up @@ -75,12 +76,12 @@ export default defineDriver<DenoKvOptions, Promise<Deno.Kv | Kv>>(
const value = await kv.get(r(key));
return value.value;
},
async setItem(key, value, tOptions: DenoKVSetOptions) {
async setItem(key, value, tOptions?: DenoKVSetOptions) {
const ttl = normalizeTTL(tOptions?.ttl ?? opts?.ttl);
const kv = await getKv();
await kv.set(r(key), value, { expireIn: ttl });
},
async setItemRaw(key, value, tOptions: DenoKVSetOptions) {
async setItemRaw(key, value, tOptions?: DenoKVSetOptions) {
const ttl = normalizeTTL(tOptions?.ttl ?? opts?.ttl);
const kv = await getKv();
await kv.set(r(key), value, { expireIn: ttl });
Expand Down
21 changes: 15 additions & 6 deletions src/drivers/netlify-blobs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createError, createRequiredError, defineDriver } from "./utils";
import type { GetKeysOptions } from "..";
import { getStore, getDeployStore } from "@netlify/blobs";
import type {
Store,
Expand Down Expand Up @@ -46,7 +45,20 @@ export interface NetlifyNamedStoreOptions
deployScoped?: false;
}

export default defineDriver((options: NetlifyStoreOptions) => {
export interface NetlifyGetKeysOptions
extends Omit<ListOptions, "prefix" | "paginate"> {
maxDepth?: number;
}

export default defineDriver<
NetlifyStoreOptions,
Store,
{
setOptions: SetOptions;
getOptions: GetOptions;
getKeysOptions: NetlifyGetKeysOptions;
}
>((options: NetlifyStoreOptions) => {
const { deployScoped, name, ...opts } = options;
let store: Store;

Expand Down Expand Up @@ -107,10 +119,7 @@ export default defineDriver((options: NetlifyStoreOptions) => {
removeItem(key) {
return getClient().delete(key);
},
async getKeys(
base?: string,
tops?: GetKeysOptions & Omit<ListOptions, "prefix" | "paginate">
) {
async getKeys(base?: string, tops?: NetlifyGetKeysOptions) {
return (await getClient().list({ ...tops, prefix: base })).blobs.map(
(item) => item.key
);
Expand Down
22 changes: 16 additions & 6 deletions src/drivers/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import type { Driver } from "../..";
import type { Driver, DriverMethodOptionsMap } from "../..";

type DriverFactory<OptionsT, InstanceT> = (
type DriverFactory<
OptionsT,
InstanceT,
DriverMethodOptionsMapT extends DriverMethodOptionsMap,
// Partial here, to not required typed options to be optional in type argument
> = (
opts: OptionsT
) => Driver<OptionsT, InstanceT>;
) => Driver<OptionsT, InstanceT, Partial<DriverMethodOptionsMapT>>;
interface ErrorOptions {}

export function defineDriver<OptionsT = any, InstanceT = never>(
factory: DriverFactory<OptionsT, InstanceT>
): DriverFactory<OptionsT, InstanceT> {
export function defineDriver<
OptionsT = any,
InstanceT = never,
DriverMethodOptionsMapT extends
DriverMethodOptionsMap = DriverMethodOptionsMap,
>(
factory: DriverFactory<OptionsT, InstanceT, DriverMethodOptionsMapT>
): DriverFactory<OptionsT, InstanceT, DriverMethodOptionsMapT> {
return factory;
}

Expand Down
11 changes: 6 additions & 5 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ interface StorageCTX {
watchListeners: ((event: WatchEvent, key: string) => void)[];
}

export interface CreateStorageOptions {
driver?: Driver;
export interface CreateStorageOptions<DriverT extends Driver = Driver> {
driver?: DriverT;
}

export function createStorage<T extends StorageValue>(
options: CreateStorageOptions = {}
): Storage<T> {
export function createStorage<
T extends StorageValue,
DriverT extends Driver = Driver,
>(options: CreateStorageOptions<DriverT> = {}): Storage<T, DriverT> {
const context: StorageCTX = {
mounts: { "": options.driver || memory() },
mountpoints: [""],
Expand Down
154 changes: 110 additions & 44 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,58 +15,91 @@ export interface StorageMeta {
[key: string]: StorageValue | Date | undefined;
}

// TODO: type ttl
export type TransactionOptions = Record<string, any>;

export type GetKeysOptions = TransactionOptions & {
maxDepth?: number;
};

export interface DriverFlags {
maxDepth?: boolean;
ttl?: boolean;
}

export interface Driver<OptionsT = any, InstanceT = any> {
export type DriverMethodOptionsMap<
GetOptionsT = TransactionOptions,
SetOptionsT = TransactionOptions,
HasOptionsT = TransactionOptions,
RemoveOptionsT = TransactionOptions,
GetKeysOptionsT = TransactionOptions,
> = {
getOptions?: GetOptionsT;
setOptions?: SetOptionsT;
hasOptions?: HasOptionsT;
removeOptions?: RemoveOptionsT;
getKeysOptions?: GetKeysOptionsT;
};

export interface Driver<
OptionsT = any,
InstanceT = any,
DriverMethodOptionsMapT extends
DriverMethodOptionsMap = DriverMethodOptionsMap,
> {
name?: string;
flags?: DriverFlags;
options?: OptionsT;
getInstance?: () => InstanceT;
hasItem: (key: string, opts: TransactionOptions) => MaybePromise<boolean>;
hasItem: (
key: string,
opts: DriverMethodOptionsMapT["hasOptions"]
) => MaybePromise<boolean>;
getItem: (
key: string,
opts?: TransactionOptions
opts?: DriverMethodOptionsMapT["getOptions"]
) => MaybePromise<StorageValue>;
/** @experimental */
getItems?: (
items: { key: string; options?: TransactionOptions }[],
commonOptions?: TransactionOptions
items: { key: string; options?: DriverMethodOptionsMapT["getOptions"] }[],
commonOptions?: DriverMethodOptionsMapT["getOptions"]
) => MaybePromise<{ key: string; value: StorageValue }[]>;
/** @experimental */
getItemRaw?: (key: string, opts: TransactionOptions) => MaybePromise<unknown>;
getItemRaw?: (
key: string,
opts: DriverMethodOptionsMapT["getOptions"]
) => MaybePromise<unknown>;
setItem?: (
key: string,
value: string,
opts: TransactionOptions
opts: DriverMethodOptionsMapT["setOptions"]
) => MaybePromise<void>;
/** @experimental */
setItems?: (
items: { key: string; value: string; options?: TransactionOptions }[],
commonOptions?: TransactionOptions
items: {
key: string;
value: string;
options?: DriverMethodOptionsMapT["setOptions"];
}[],
commonOptions?: DriverMethodOptionsMapT["setOptions"]
) => MaybePromise<void>;
/** @experimental */
setItemRaw?: (
key: string,
value: any,
opts: TransactionOptions
opts: DriverMethodOptionsMapT["setOptions"]
) => MaybePromise<void>;
removeItem?: (
key: string,
opts: DriverMethodOptionsMapT["removeOptions"]
) => MaybePromise<void>;
removeItem?: (key: string, opts: TransactionOptions) => MaybePromise<void>;
getMeta?: (
key: string,
opts: TransactionOptions
opts: DriverMethodOptionsMapT["getOptions"]
) => MaybePromise<StorageMeta | null>;
getKeys: (base: string, opts: GetKeysOptions) => MaybePromise<string[]>;
clear?: (base: string, opts: TransactionOptions) => MaybePromise<void>;
getKeys: (
base: string,
opts: DriverMethodOptionsMapT["getKeysOptions"]
) => MaybePromise<string[]>;
clear?: (
base: string,
opts: DriverMethodOptionsMapT["removeOptions"]
) => MaybePromise<void>;
dispose?: () => MaybePromise<void>;
watch?: (callback: WatchCallback) => MaybePromise<Unwatch>;
}
Expand All @@ -83,38 +116,68 @@ type StorageItemType<T, K> = K extends keyof StorageItemMap<T>
? StorageValue
: T;

export interface Storage<T extends StorageValue = StorageValue> {
type StorageMethodOptions<DriverT extends Driver> =
DriverT extends Driver<any, any, infer MethodOptionsT>
? MethodOptionsT
: never;

// if options type is not set it is unknown and we default back to TransactionOptions
type SetOptionsType<DriverT extends Driver> =
unknown extends StorageMethodOptions<DriverT>["setOptions"]
? TransactionOptions
: StorageMethodOptions<DriverT>["setOptions"] | undefined;
type GetOptionsType<DriverT extends Driver> =
unknown extends StorageMethodOptions<DriverT>["getOptions"]
? TransactionOptions
: StorageMethodOptions<DriverT>["getOptions"];
type RemoveOptionsType<DriverT extends Driver> =
unknown extends StorageMethodOptions<DriverT>["removeOptions"]
? TransactionOptions
: StorageMethodOptions<DriverT>["removeOptions"];
type GetKeysOptionsType<DriverT extends Driver> =
unknown extends StorageMethodOptions<DriverT>["getKeysOptions"]
? TransactionOptions
: StorageMethodOptions<DriverT>["getKeysOptions"];
type HasOptionsType<DriverT extends Driver> =
unknown extends StorageMethodOptions<DriverT>["hasOptions"]
? TransactionOptions
: StorageMethodOptions<DriverT>["hasOptions"];

export interface Storage<
T extends StorageValue = StorageValue,
DriverT extends Driver = Driver,
> {
// Item
hasItem<
U extends Extract<T, StorageDefinition>,
K extends keyof StorageItemMap<U>,
>(
key: K,
opts?: TransactionOptions
opts?: HasOptionsType<DriverT>
): Promise<boolean>;
hasItem(key: string, opts?: TransactionOptions): Promise<boolean>;
hasItem(key: string, opts?: HasOptionsType<DriverT>): Promise<boolean>;

getItem<
U extends Extract<T, StorageDefinition>,
K extends string & keyof StorageItemMap<U>,
>(
key: K,
ops?: TransactionOptions
opts?: GetOptionsType<DriverT>
): Promise<StorageItemType<T, K> | null>;
getItem<R = StorageItemType<T, string>>(
key: string,
opts?: TransactionOptions
opts?: GetOptionsType<DriverT>
): Promise<R | null>;

/** @experimental */
getItems: <U extends T>(
items: (string | { key: string; options?: TransactionOptions })[],
commonOptions?: TransactionOptions
items: (string | { key: string; options?: GetOptionsType<DriverT> })[],
commonOptions?: GetOptionsType<DriverT>
) => Promise<{ key: string; value: U }[]>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
getItemRaw: <T = any>(
key: string,
opts?: TransactionOptions
opts?: GetOptionsType<DriverT>
) => Promise<MaybeDefined<T> | null>;

setItem<
Expand All @@ -123,24 +186,24 @@ export interface Storage<T extends StorageValue = StorageValue> {
>(
key: K,
value: StorageItemType<T, K>,
opts?: TransactionOptions
opts?: SetOptionsType<DriverT>
): Promise<void>;
setItem<U extends T>(
key: string,
value: U,
opts?: TransactionOptions
opts?: SetOptionsType<DriverT>
): Promise<void>;

/** @experimental */
setItems: <U extends T>(
items: { key: string; value: U; options?: TransactionOptions }[],
commonOptions?: TransactionOptions
items: { key: string; value: U; options?: SetOptionsType<DriverT> }[],
commonOptions?: SetOptionsType<DriverT>
) => Promise<void>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
setItemRaw: <T = any>(
key: string,
value: MaybeDefined<T>,
opts?: TransactionOptions
opts?: SetOptionsType<DriverT>
) => Promise<void>;

removeItem<
Expand All @@ -149,33 +212,36 @@ export interface Storage<T extends StorageValue = StorageValue> {
>(
key: K,
opts?:
| (TransactionOptions & { removeMeta?: boolean })
| (RemoveOptionsType<DriverT> & { removeMeta?: boolean })
| boolean /* legacy: removeMeta */
): Promise<void>;
removeItem(
key: string,
opts?:
| (TransactionOptions & { removeMeta?: boolean })
| (RemoveOptionsType<DriverT> & { removeMeta?: boolean })
| boolean /* legacy: removeMeta */
): Promise<void>;

// Meta
getMeta: (
key: string,
opts?:
| (TransactionOptions & { nativeOnly?: boolean })
| (GetOptionsType<DriverT> & { nativeOnly?: boolean })
| boolean /* legacy: nativeOnly */
) => MaybePromise<StorageMeta>;
setMeta: (
key: string,
value: StorageMeta,
opts?: TransactionOptions
opts?: SetOptionsType<DriverT>
) => Promise<void>;
removeMeta: (key: string, opts?: TransactionOptions) => Promise<void>;
removeMeta: (key: string, opts?: RemoveOptionsType<DriverT>) => Promise<void>;
// Keys
getKeys: (base?: string, opts?: GetKeysOptions) => Promise<string[]>;
getKeys: (
base?: string,
opts?: GetKeysOptionsType<DriverT>
) => Promise<string[]>;
// Utils
clear: (base?: string, opts?: TransactionOptions) => Promise<void>;
clear: (base?: string, opts?: RemoveOptionsType<DriverT>) => Promise<void>;
dispose: () => Promise<void>;
watch: (callback: WatchCallback) => Promise<Unwatch>;
unwatch: () => Promise<void>;
Expand All @@ -189,9 +255,9 @@ export interface Storage<T extends StorageValue = StorageValue> {
) => { base: string; driver: Driver }[];
// Aliases
keys: Storage["getKeys"];
get: Storage<T>["getItem"];
set: Storage<T>["setItem"];
has: Storage<T>["hasItem"];
del: Storage<T>["removeItem"];
remove: Storage<T>["removeItem"];
get: Storage<T, DriverT>["getItem"];
set: Storage<T, DriverT>["setItem"];
has: Storage<T, DriverT>["hasItem"];
del: Storage<T, DriverT>["removeItem"];
remove: Storage<T, DriverT>["removeItem"];
}
2 changes: 1 addition & 1 deletion test/drivers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface TestContext {
}

export interface TestOptions {
driver: Driver | (() => Driver);
driver: Driver<any, any, any> | (() => Driver<any, any, any>);
noKeysSupport?: boolean;
additionalTests?: (ctx: TestContext) => void;
}
Expand Down