Skip to content

feat: support getItem(key, { type }) #610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
43 changes: 38 additions & 5 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,44 @@
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.hasItem, relativeKey, opts);
},
getItem(key: string, opts = {}) {
getItem: async (
key: string,
opts: TransactionOptions & {
type?: "json" | "text" | "bytes" | "stream" | "blob";
} = {}
) => {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.getItem, relativeKey, opts).then(
(value) => destr(value) as StorageValue
);

const type = opts.type;

if (type === "bytes" || type === "stream" || type === "blob") {
if (driver.getItemRaw) {
return asyncCall(driver.getItemRaw, relativeKey, opts);
}
const raw = await asyncCall(driver.getItem, relativeKey, opts);
return deserializeRaw(raw);

Check warning on line 191 in src/storage.ts

View check run for this annotation

Codecov / codecov/patch

src/storage.ts#L190-L191

Added lines #L190 - L191 were not covered by tests
}

const value = await asyncCall(driver.getItem, relativeKey, opts);

if (value == null) {
return null;
}

if (type === "text") {
return typeof value === "string" ? value : String(value);
}

if (type === "json") {
if (typeof value === "string") {
return JSON.parse(value);
}
return value;

Check warning on line 208 in src/storage.ts

View check run for this annotation

Codecov / codecov/patch

src/storage.ts#L208

Added line #L208 was not covered by tests
}

// default behavior: try destr (safe JSON parse fallback to text)
return destr(value);
},
getItems(
items: (string | { key: string; options?: TransactionOptions })[],
Expand Down Expand Up @@ -473,7 +505,8 @@
},
// Aliases
keys: (base, opts = {}) => storage.getKeys(base, opts),
get: (key: string, opts = {}) => storage.getItem(key, opts),
get: ((key: string, opts: TransactionOptions = {}) =>
storage.getItem(key, opts)) as Storage<T>["get"],
set: (key: string, value: T, opts = {}) =>
storage.setItem(key, value, opts),
has: (key: string, opts = {}) => storage.hasItem(key, opts),
Expand Down
33 changes: 30 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,40 @@ export interface Storage<T extends StorageValue = StorageValue> {

getItem<
U extends Extract<T, StorageDefinition>,
K extends string & keyof StorageItemMap<U>,
K extends keyof StorageItemMap<U>,
>(
key: K,
ops?: TransactionOptions
opts?: TransactionOptions & { type?: undefined }
): Promise<StorageItemType<T, K> | null>;

getItem(
key: string,
opts: { type: "text" } & TransactionOptions
): Promise<string | null>;

getItem(
key: string,
opts: { type: "json" } & TransactionOptions
): Promise<unknown | null>;

getItem(
key: string,
opts: { type: "bytes" } & TransactionOptions
): Promise<Uint8Array | null>;

getItem(
key: string,
opts: { type: "stream" } & TransactionOptions
): Promise<ReadableStream | null>;

getItem(
key: string,
opts: { type: "blob" } & TransactionOptions
): Promise<Blob | null>;

getItem<R = StorageItemType<T, string>>(
key: string,
opts?: TransactionOptions
opts?: TransactionOptions & { type?: undefined }
): Promise<R | null>;

/** @experimental */
Expand Down Expand Up @@ -190,6 +216,7 @@ export interface Storage<T extends StorageValue = StorageValue> {
// Aliases
keys: Storage["getKeys"];
get: Storage<T>["getItem"];

set: Storage<T>["setItem"];
has: Storage<T>["hasItem"];
del: Storage<T>["removeItem"];
Expand Down
25 changes: 25 additions & 0 deletions test/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,28 @@ describe("Regression", () => {
}
});
});

describe("get() with type option", () => {
const storage = createStorage();

it("should get JSON object with type=json", async () => {
const obj = { foo: "bar", num: 42 };
await storage.setItem("json-key", obj);
const result = await storage.get("json-key", { type: "json" });
expect(result).toEqual(obj);
});

it("should get string with type=text", async () => {
await storage.setItem("text-key", "hello world");
const result = await storage.get("text-key", { type: "text" });
expect(result).toBe("hello world");
});

it("should get bytes with type=bytes", async () => {
const bytes = new Uint8Array([1, 2, 3, 4]);
await storage.setItemRaw("bytes-key", bytes);
const result = await storage.get("bytes-key", { type: "bytes" });
const len = result?.length || result?.byteLength;
expect(len).toBe(4);
});
});