Skip to content

Commit

Permalink
feat: allow passing transaction options to drivers (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Feb 28, 2023
1 parent 002a604 commit f97b449
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 70 deletions.
22 changes: 11 additions & 11 deletions docs/content/2.usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,23 @@ await storage.getItem("foo:bar"); // or storage.getItem('/foo/bar')

## Interface

### `hasItem(key)`
### `hasItem(key, opts?)`

Checks if storage contains a key. Resolves to either `true` or `false`.

```js
await storage.hasItem("foo:bar");
```

### `getItem(key)`
### `getItem(key, opts?)`

Gets the value of a key in storage. Resolves to either a javascript primitive value or `undefined`.

```js
await storage.getItem("foo:bar");
```

### `getItemRaw(key)`
### `getItemRaw(key, opts?)`

**Note:** This is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.

Expand All @@ -61,7 +61,7 @@ Gets the value of a key in storage in raw format.
const value = await storage.getItemRaw("foo:bar.bin");
```

### `setItem(key, value)`
### `setItem(key, value, opts?)`

Add/Update a value to the storage.

Expand All @@ -73,7 +73,7 @@ If value is `undefined`, it is same as calling `removeItem(key)`.
await storage.setItem("foo:bar", "baz");
```

### `setItemRaw(key, value)`
### `setItemRaw(key, value, opts?)`

**Note:** This is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.

Expand All @@ -85,15 +85,15 @@ If value is `undefined`, it is same as calling `removeItem(key)`.
await storage.setItemRaw("data/test.bin", new Uint8Array([1, 2, 3]));
```

### `removeItem(key, removeMeta = true)`
### `removeItem(key, opts = { removeMeta = true })`

Remove a value (and it's meta) from storage.

```js
await storage.removeItem("foo:bar");
```

### `getMeta(key, nativeOnly?)`
### `getMeta(key, opts = { nativeOnly? })`

Get metadata object for a specific key.

Expand All @@ -106,7 +106,7 @@ This data is fetched from two sources:
await storage.getMeta("foo:bar"); // For fs driver returns an object like { mtime, atime, size }
```

### `setMeta(key)`
### `setMeta(key, opts?)`

Set custom meta for a specific key by adding a `$` suffix.

Expand All @@ -115,7 +115,7 @@ await storage.setMeta("foo:bar", { flag: 1 });
// Same as storage.setItem('foo:bar$', { flag: 1 })
```

### `removeMeta(key)`
### `removeMeta(key, opts?)`

Remove meta for a specific key by adding a `$` suffix.

Expand All @@ -124,7 +124,7 @@ await storage.removeMeta("foo:bar");
// Same as storage.removeItem('foo:bar$')
```

### `getKeys(base?)`
### `getKeys(base?, opts?)`

Get all keys. Returns an array of strings.

Expand All @@ -136,7 +136,7 @@ If a base is provided, only keys starting with the base will be returned also on
await storage.getKeys();
```

### `clear(base?)`
### `clear(base?, opts?)`

Removes all stored key/values. If a base is provided, only mounts matching base will be cleared.

Expand Down
14 changes: 7 additions & 7 deletions docs/content/5.custom-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { createStorage, defineDriver } from "unstorage";

const myStorageDriver = defineDriver((_opts) => {
return {
async hasItem(key) {},
async getItem(key) {},
async setItem(key, value) {},
async removeItem(key) {},
async getKeys() {},
async clear() {},
async hasItem(key, opts) {},
async getItem(key, opts) {},
async setItem(key, value, opts) {},
async removeItem(key, opts) {},
async getKeys(base, opts) {},
async clear(base, opts) {},
async dispose() {},
// async watch(callback) {}
async watch(callback) {},
};
});

Expand Down
75 changes: 43 additions & 32 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,29 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {

const storage: Storage = {
// Item
hasItem(key) {
hasItem(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.hasItem, relativeKey);
return asyncCall(driver.hasItem, relativeKey, opts);
},
getItem(key) {
getItem(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.getItem, relativeKey).then((value) =>
return asyncCall(driver.getItem, relativeKey, opts).then((value) =>
destr(value)
);
},
getItemRaw(key) {
getItemRaw(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (driver.getItemRaw) {
return asyncCall(driver.getItemRaw, relativeKey);
return asyncCall(driver.getItemRaw, relativeKey, opts);
}
return asyncCall(driver.getItem, relativeKey).then((value) =>
return asyncCall(driver.getItem, relativeKey, opts).then((value) =>
deserializeRaw(value)
);
},
async setItem(key, value) {
async setItem(key, value, opts = {}) {
if (value === undefined) {
return storage.removeItem(key);
}
Expand All @@ -134,54 +134,64 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
if (!driver.setItem) {
return; // Readonly
}
await asyncCall(driver.setItem, relativeKey, stringify(value));
await asyncCall(driver.setItem, relativeKey, stringify(value), opts);
if (!driver.watch) {
onChange("update", key);
}
},
async setItemRaw(key, value) {
async setItemRaw(key, value, opts = {}) {
if (value === undefined) {
return storage.removeItem(key);
return storage.removeItem(key, opts);
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (driver.setItemRaw) {
await asyncCall(driver.setItemRaw, relativeKey, value);
await asyncCall(driver.setItemRaw, relativeKey, value, opts);
} else if (driver.setItem) {
await asyncCall(driver.setItem, relativeKey, serializeRaw(value));
await asyncCall(driver.setItem, relativeKey, serializeRaw(value), opts);
} else {
return; // Readonly
}
if (!driver.watch) {
onChange("update", key);
}
},
async removeItem(key, removeMeta = true) {
async removeItem(key, opts = {}) {
// TODO: Remove in next major version
if (typeof opts === "boolean") {
opts = { removeMata: opts };
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (!driver.removeItem) {
return; // Readonly
}
await asyncCall(driver.removeItem, relativeKey);
if (removeMeta) {
await asyncCall(driver.removeItem, relativeKey + "$");
await asyncCall(driver.removeItem, relativeKey, opts);
if (opts.removeMata) {
await asyncCall(driver.removeItem, relativeKey + "$", opts);
}
if (!driver.watch) {
onChange("remove", key);
}
},
// Meta
async getMeta(key, nativeMetaOnly) {
async getMeta(key, opts = {}) {
// TODO: Remove in next major version
if (typeof opts === "boolean") {
opts = { nativeOnly: opts };
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
const meta = Object.create(null);
if (driver.getMeta) {
Object.assign(meta, await asyncCall(driver.getMeta, relativeKey));
Object.assign(meta, await asyncCall(driver.getMeta, relativeKey, opts));
}
if (!nativeMetaOnly) {
const value = await asyncCall(driver.getItem, relativeKey + "$").then(
(value_) => destr(value_)
);
if (!opts.nativeOnly) {
const value = await asyncCall(
driver.getItem,
relativeKey + "$",
opts
).then((value_) => destr(value_));
if (value && typeof value === "object") {
// TODO: Support date by destr?
if (typeof value.atime === "string") {
Expand All @@ -195,22 +205,23 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
}
return meta;
},
setMeta(key: string, value: any) {
return this.setItem(key + "$", value);
setMeta(key: string, value: any, opts = {}) {
return this.setItem(key + "$", value, opts);
},
removeMeta(key: string) {
return this.removeItem(key + "$");
removeMeta(key: string, opts = {}) {
return this.removeItem(key + "$", opts);
},
// Keys
async getKeys(base) {
async getKeys(base, opts = {}) {
base = normalizeBaseKey(base);
const mounts = getMounts(base, true);
let maskedMounts = [];
const allKeys = [];
for (const mount of mounts) {
const rawKeys = await asyncCall(
mount.driver.getKeys,
mount.relativeBase
mount.relativeBase,
opts
);
const keys = rawKeys
.map((key) => mount.mountpoint + normalizeKey(key))
Expand All @@ -229,16 +240,16 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
: allKeys.filter((key) => !key.endsWith("$"));
},
// Utils
async clear(base) {
async clear(base, opts = {}) {
base = normalizeBaseKey(base);
await Promise.all(
getMounts(base, false).map(async (m) => {
if (m.driver.clear) {
return asyncCall(m.driver.clear);
return asyncCall(m.driver.clear, m.relativeBase, opts);
}
// Fallback to remove all keys if clear not implemented
if (m.driver.removeItem) {
const keys = await m.driver.getKeys();
const keys = await m.driver.getKeys(m.relativeBase, opts);
return Promise.all(keys.map((key) => m.driver.removeItem!(key)));
}
// Readonly
Expand Down
81 changes: 61 additions & 20 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,81 @@ export interface StorageMeta {
[key: string]: StorageValue | Date | undefined;
}

type TransactionOptions = Record<string, any>;

export interface Driver {
hasItem: (key: string) => MaybePromise<boolean>;
getItem: (key: string) => MaybePromise<StorageValue>;
hasItem: (key: string, opts?: TransactionOptions) => MaybePromise<boolean>;
getItem: (
key: string,
opts?: TransactionOptions
) => MaybePromise<StorageValue>;
/** @experimental */
getItemRaw?: (key: string) => MaybePromise<unknown>;
setItem?: (key: string, value: string) => MaybePromise<void>;
getItemRaw?: (
key: string,
opts?: TransactionOptions
) => MaybePromise<unknown>;
setItem?: (
key: string,
value: string,
opts?: TransactionOptions
) => MaybePromise<void>;
/** @experimental */
setItemRaw?: (key: string, value: any) => MaybePromise<void>;
removeItem?: (key: string) => MaybePromise<void>;
getMeta?: (key: string) => MaybePromise<StorageMeta>;
getKeys: (base?: string) => MaybePromise<string[]>;
clear?: () => MaybePromise<void>;
setItemRaw?: (
key: string,
value: any,
opts?: TransactionOptions
) => MaybePromise<void>;
removeItem?: (key: string, opts?: TransactionOptions) => MaybePromise<void>;
getMeta?: (
key: string,
opts?: TransactionOptions
) => MaybePromise<StorageMeta>;
getKeys: (base?: string, opts?: TransactionOptions) => MaybePromise<string[]>;
clear?: (base?: string, opts?: TransactionOptions) => MaybePromise<void>;
dispose?: () => MaybePromise<void>;
watch?: (callback: WatchCallback) => MaybePromise<Unwatch>;
}

export interface Storage {
// Item
hasItem: (key: string) => Promise<boolean>;
getItem: (key: string) => Promise<StorageValue>;
hasItem: (key: string, opts?: TransactionOptions) => Promise<boolean>;
getItem: (key: string, opts?: TransactionOptions) => Promise<StorageValue>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
getItemRaw: (key: string) => Promise<any>;
setItem: (key: string, value: StorageValue) => Promise<void>;
getItemRaw: (key: string, opts?: TransactionOptions) => Promise<any>;
setItem: (
key: string,
value: StorageValue,
opts?: TransactionOptions
) => Promise<void>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
setItemRaw: (key: string, value: any) => Promise<void>;
removeItem: (key: string, removeMeta?: boolean) => Promise<void>;
setItemRaw: (
key: string,
value: any,
opts?: TransactionOptions
) => Promise<void>;
removeItem: (
key: string,
opts?:
| (TransactionOptions & { removeMata?: boolean })
| boolean /* legacy: removeMata */
) => Promise<void>;
// Meta
getMeta: (key: string, nativeMetaOnly?: true) => MaybePromise<StorageMeta>;
setMeta: (key: string, value: StorageMeta) => Promise<void>;
removeMeta: (key: string) => Promise<void>;
getMeta: (
key: string,
opts?:
| (TransactionOptions & { nativeOnly?: boolean })
| boolean /* legacy: nativeOnly */
) => MaybePromise<StorageMeta>;
setMeta: (
key: string,
value: StorageMeta,
opts?: TransactionOptions
) => Promise<void>;
removeMeta: (key: string, opts?: TransactionOptions) => Promise<void>;
// Keys
getKeys: (base?: string) => Promise<string[]>;
getKeys: (base?: string, opts?: TransactionOptions) => Promise<string[]>;
// Utils
clear: (base?: string) => Promise<void>;
clear: (base?: string, opts?: TransactionOptions) => Promise<void>;
dispose: () => Promise<void>;
watch: (callback: WatchCallback) => Promise<Unwatch>;
unwatch: () => Promise<void>;
Expand Down

1 comment on commit f97b449

@vercel
Copy link

@vercel vercel bot commented on f97b449 Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.