Skip to content

Commit 750ad8e

Browse files
authored
Merge pull request #16 from prettygoodtech/feature/more-object-behavior-features
Add ownKeys, getOwnPropertyDescriptor to proxy
2 parents 1f4ac65 + 72f64a9 commit 750ad8e

File tree

4 files changed

+127
-2
lines changed

4 files changed

+127
-2
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning][semver].
77

88
## [Unreleased]
99

10+
### Added
11+
- Support for the following `Object` static methods:
12+
- `keys`
13+
- `values`
14+
- `entries`
15+
- `getOwnPropertyNames`
16+
- `getOwnPropertyDescriptor`
17+
- `getOwnPropertyDescriptors`
18+
1019
## [0.3.0] - 2023-03-16
1120
### Added
1221
- "Features" section to README.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ for Node.js.
1414
- Supports `setItem`, `getItem`, `deleteItem`, `clear`, and `key` methods.
1515
- Supports `length` property.
1616
- Supports dot and bracket property accessors.
17+
- Supports `Object.keys`, `Object.values`, and `Object.entries` static methods.
1718
- Small footprint with zero dependencies.
1819
- TypeScript declarations.
1920

src/index.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,87 @@ test.serial(
213213
t.is(typeof nsStorage["setItem"], "function");
214214
}
215215
);
216+
217+
test.serial("NamespacedStorage - Object.keys", (t) => {
218+
t.context.storage.setItem("foo:user-id", "1234");
219+
t.context.storage.setItem("banner-closed", "true");
220+
t.context.storage.setItem("foo:preferred-theme", "dark");
221+
222+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
223+
224+
t.deepEqual(Object.keys(nsStorage), ["user-id", "preferred-theme"]);
225+
});
226+
227+
test.serial("NamespacedStorage - Object.values", (t) => {
228+
t.context.storage.setItem("foo:user-id", "1234");
229+
t.context.storage.setItem("banner-closed", "true");
230+
t.context.storage.setItem("foo:preferred-theme", "dark");
231+
232+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
233+
234+
t.deepEqual(Object.values(nsStorage), ["1234", "dark"]);
235+
});
236+
237+
test.serial("NamespacedStorage - Object.entries", (t) => {
238+
t.context.storage.setItem("foo:user-id", "1234");
239+
t.context.storage.setItem("banner-closed", "true");
240+
t.context.storage.setItem("foo:preferred-theme", "dark");
241+
242+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
243+
244+
t.deepEqual(Object.entries(nsStorage), [
245+
["user-id", "1234"],
246+
["preferred-theme", "dark"],
247+
]);
248+
});
249+
250+
test.serial("NamespacedStorage - Object.getOwnPropertyNames", (t) => {
251+
t.context.storage.setItem("foo:user-id", "1234");
252+
t.context.storage.setItem("banner-closed", "true");
253+
t.context.storage.setItem("foo:preferred-theme", "dark");
254+
255+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
256+
257+
t.deepEqual(Object.getOwnPropertyNames(nsStorage), [
258+
"user-id",
259+
"preferred-theme",
260+
]);
261+
});
262+
263+
test.serial("NamespacedStorage - Object.getOwnPropertyDescriptor", (t) => {
264+
t.context.storage.setItem("foo:user-id", "1234");
265+
t.context.storage.setItem("banner-closed", "true");
266+
t.context.storage.setItem("foo:preferred-theme", "dark");
267+
268+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
269+
270+
t.deepEqual(Object.getOwnPropertyDescriptor(nsStorage, "preferred-theme"), {
271+
configurable: true,
272+
enumerable: true,
273+
value: "dark",
274+
writable: true,
275+
});
276+
});
277+
278+
test.serial("NamespacedStorage - Object.getOwnPropertyDescriptors", (t) => {
279+
t.context.storage.setItem("foo:user-id", "1234");
280+
t.context.storage.setItem("banner-closed", "true");
281+
t.context.storage.setItem("foo:preferred-theme", "dark");
282+
283+
const nsStorage = new NamespacedStorage(t.context.storage, "foo");
284+
285+
t.deepEqual(Object.getOwnPropertyDescriptors(nsStorage), {
286+
"user-id": {
287+
configurable: true,
288+
enumerable: true,
289+
value: "1234",
290+
writable: true,
291+
},
292+
"preferred-theme": {
293+
configurable: true,
294+
enumerable: true,
295+
value: "dark",
296+
writable: true,
297+
},
298+
});
299+
});

src/index.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const RESERVED_PROPERTIES = [
4545
"getNamespacedEntries",
4646
];
4747

48-
const propertyAccessorHandler: ProxyHandler<NamespacedStorage> = {
48+
const proxyHandler: ProxyHandler<NamespacedStorage> = {
4949
// Necessary for:
5050
// - nsStorage.foo = "bar"
5151
// - nsStorage["foo"] = "bar"
@@ -82,6 +82,37 @@ const propertyAccessorHandler: ProxyHandler<NamespacedStorage> = {
8282
target.removeItem(property);
8383
return true;
8484
},
85+
86+
ownKeys: (target): string[] => {
87+
const keys: string[] = [];
88+
for (let i = 0; i < target.length; i++) {
89+
keys.push(target.key(i)!);
90+
}
91+
92+
return keys;
93+
},
94+
95+
getOwnPropertyDescriptor: (
96+
target,
97+
property: string
98+
): PropertyDescriptor | undefined => {
99+
if (RESERVED_PROPERTIES.includes(property)) {
100+
return;
101+
}
102+
103+
const value = target.getItem(property);
104+
105+
if (!value) {
106+
return;
107+
}
108+
109+
return {
110+
value,
111+
configurable: true,
112+
enumerable: true,
113+
writable: true,
114+
};
115+
},
85116
};
86117

87118
export class NamespacedStorage implements Storage {
@@ -95,7 +126,7 @@ export class NamespacedStorage implements Storage {
95126
* `prefix` to create a namespace for keys.
96127
*/
97128
constructor(private storage: Storage, private prefix: string) {
98-
return new Proxy(this, propertyAccessorHandler);
129+
return new Proxy(this, proxyHandler);
99130
}
100131

101132
/** Returns the number of key/value pairs in the namespace. */

0 commit comments

Comments
 (0)