Skip to content

Commit

Permalink
feat: add key equals and startsWith
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Dec 17, 2023
1 parent 2b32547 commit 8634d58
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 4 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ in a noop.

APIs for dealing with Deno KV keys.

### `equals()`

Compares the quality of two `Deno.KvKey`s, returning `true` if they are equal
and `false` if they are not. This is more specialized than other forms of deeply
equal comparison.

### `startsWith()`

Determines if the `key` starts with the `prefix` provided, returning `true` if
it does, otherwise `false`.

### `keys()`

Similar to `Deno.Kv.prototype.list()`, in that is takes a selector, but instead
Expand Down
54 changes: 53 additions & 1 deletion keys.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, assertEquals, setup, teardown } from "./_test_util.ts";

import { keys, unique, uniqueCount } from "./keys.ts";
import { equals, keys, startsWith, unique, uniqueCount } from "./keys.ts";

Deno.test({
name: "keys - returns a list of keys",
Expand Down Expand Up @@ -111,3 +111,55 @@ Deno.test({
return teardown();
},
});

Deno.test({
name: "equals",
fn() {
assert(equals(["a"], ["a"]));
assert(!equals(["a"], ["b"]));
assert(equals([1], [1]));
assert(!equals([1], ["1"]));
assert(
equals(["a", 1, 1n, true, new Uint8Array([1, 2, 3])], [
"a",
1,
1n,
true,
new Uint8Array([1, 2, 3]),
]),
);
assert(!equals(["a", 1n, 1, true], ["a", 1, 1n, true]));
assert(
!equals(["a", 1, 1n, true, new Uint8Array([3, 2, 1])], [
"a",
1,
1n,
true,
new Uint8Array([1, 2, 3]),
]),
);
},
});

Deno.test({
name: "startsWith",
fn() {
assert(startsWith(["a", "b"], ["a"]));
assert(startsWith(["a", "b"], ["a", "b"]));
assert(!startsWith(["a"], ["a", "b"]));
assert(
startsWith(["a", new Uint8Array([1, 2, 3]), 1, 1n, true], [
"a",
new Uint8Array([1, 2, 3]),
]),
);
assert(
!startsWith(["a", new Uint8Array([1, 2, 3]), 1, 1n, true], [
"a",
new Uint8Array([1, 2, 3]),
1,
2n,
]),
);
},
});
64 changes: 61 additions & 3 deletions keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,71 @@ function addOrIncrement(
map.set(item, increment ? 1 : 0);
}

/** Determines if one {@linkcode Deno.KvKey} equals another. This is more
* focused than a deeply equals comparison and compares key parts that are
* `Uint8Array` in a way that avoids potential code exploits.
*
* ### Example
*
* ```ts
* import { equals } from "https://deno.land/x/kv_toolbox/keys.ts";
*
* const keyA = ["a", "b"];
* const keyB = ["a", "b"];
* if (equals(keyA, keyB)) {
* console.log("keys match");
* }
* ```
*/
export function equals(a: Deno.KvKey, b: Deno.KvKey): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
const partA = a[i];
const partB = b[i];
if (ArrayBuffer.isView(partA)) {
if (!ArrayBuffer.isView(partB)) {
return false;
}
if (!timingSafeEqual(partA, partB)) {
return false;
}
} else if (partA !== partB) {
return false;
}
}
return true;
}

/** Determines if one {@linkcode Deno.KvKey} matches the prefix of another.
*
* ### Example
*
* ```ts
* import { startsWith } from "https://deno.land/x/kv_toolbox/keys.ts";
*
* const key = ["a", "b"];
* const prefix = ["a"];
* if (equals(key, prefix)) {
* console.log("key starts with prefix");
* }
* ```
*/
export function startsWith(key: Deno.KvKey, prefix: Deno.KvKey): boolean {
if (prefix.length > key.length) {
return false;
}
return equals(prefix, key.slice(0, prefix.length));
}

/** Return an array of keys that match the `selector` in the target `kv`
* store.
*
* ### Example
*
* ```ts
* import { keys } from "https://deno.land/x/kv-tools/keys.ts";
* import { keys } from "https://deno.land/x/kv_toolbox/keys.ts";
*
* const kv = await Deno.openKv();
* console.log(await keys(kv, { prefix: ["hello"] }));
Expand Down Expand Up @@ -67,7 +125,7 @@ export async function keys(
* And you would get the following results when using `unique()`:
*
* ```ts
* import { unique } from "https://deno.land/x/kv-tools/keys.ts";
* import { unique } from "https://deno.land/x/kv_toolbox/keys.ts";
*
* const kv = await Deno.openKv();
* console.log(await unique(kv, ["a"]));
Expand Down Expand Up @@ -121,7 +179,7 @@ export async function unique(
* And you would get the following results when using `unique()`:
*
* ```ts
* import { uniqueCount } from "https://deno.land/x/kv-tools/keys.ts";
* import { uniqueCount } from "https://deno.land/x/kv_toolbox/keys.ts";
*
* const kv = await Deno.openKv();
* console.log(await uniqueCount(kv, ["a"]));
Expand Down

0 comments on commit 8634d58

Please sign in to comment.