run npm install to generate a package lock

This commit is contained in:
sashinexists
2024-12-07 13:18:31 +11:00
parent e7d08a91b5
commit 23437d228e
2501 changed files with 290663 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
import cache from "../../src/operations/cache.js";
describe("cache", () => {
test("caches reads of values from one tree into another", async () => {
const objectCache = new ObjectTree({});
const fixture = cache(
new DeepObjectTree({
a: 1,
b: 2,
c: 3,
more: {
d: 4,
},
}),
objectCache
);
const keys = [...(await fixture.keys())];
assert.deepEqual(keys, ["a", "b", "c", "more/"]);
assert.equal(await objectCache.get("a"), undefined);
assert.equal(await fixture.get("a"), 1);
assert.equal(await objectCache.get("a"), 1); // Now in cache
assert.equal(await objectCache.get("b"), undefined);
assert.equal(await fixture.get("b"), 2);
assert.equal(await objectCache.get("b"), 2);
assert.equal(await objectCache.get("more"), undefined);
const more = await fixture.get("more");
assert.deepEqual([...(await more.keys())], ["d"]);
assert.equal(await more.get("d"), 4);
const moreCache = await objectCache.get("more");
assert.equal(await moreCache.get("d"), 4);
});
test("if a cache filter is supplied, it only caches values whose keys match the filter", async () => {
const objectCache = new ObjectTree({});
const fixture = cache(
Tree.from({
"a.txt": "a",
"b.txt": "b",
}),
objectCache,
Tree.from({
"a.txt": true,
})
);
// Access some values to populate the cache.
assert.equal(await fixture.get("a.txt"), "a");
assert.equal(await fixture.get("b.txt"), "b");
// The a.txt value should be cached because it matches the filter.
assert.equal(await objectCache.get("a.txt"), "a");
// The b.txt value should not be cached because it does not match the filter.
assert.equal(await objectCache.get("b.txt"), undefined);
});
});

View File

@@ -0,0 +1,90 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, ObjectTree } from "../../src/internal.js";
import cachedKeyFunctions from "../../src/operations/cachedKeyFunctions.js";
describe("cachedKeyFunctions", () => {
test("maps keys with caching", async () => {
const tree = new ObjectTree({
a: "letter a",
b: "letter b",
});
let callCount = 0;
const addUnderscore = async (sourceKey, tree) => {
callCount++;
return `_${sourceKey}`;
};
const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
assert.equal(callCount, 1);
assert.equal(await inverseKey("_a", tree), "a");
assert.equal(callCount, 1);
assert.equal(await inverseKey("_b", tree), "b"); // Cache miss
assert.equal(callCount, 2);
assert.equal(await key("a", tree), "_a");
assert.equal(await key("a", tree), "_a");
assert.equal(await key("b", tree), "_b");
assert.equal(callCount, 2);
// `c` isn't in tree, so we should get undefined.
assert.equal(await inverseKey("_c", tree), undefined);
// But key mapping is still possible.
assert.equal(await key("c", tree), "_c");
// And now we have a cache hit.
assert.equal(await inverseKey("_c", tree), "c");
assert.equal(callCount, 3);
});
test("maps keys with caching and deep option", async () => {
const tree = new DeepObjectTree({
a: "letter a",
b: {
c: "letter c",
},
});
let callCount = 0;
const addUnderscore = async (sourceKey, tree) => {
callCount++;
return `_${sourceKey}`;
};
const { inverseKey, key } = cachedKeyFunctions(addUnderscore, true);
assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
assert.equal(await inverseKey("_a", tree), "a");
assert.equal(callCount, 1);
// Subtree key left alone
assert.equal(await inverseKey("_b", tree), undefined);
assert.equal(await inverseKey("b", tree), "b");
assert.equal(await inverseKey("b/", tree), "b/");
assert.equal(callCount, 1);
assert.equal(await key("a", tree), "_a");
assert.equal(await key("a", tree), "_a");
assert.equal(callCount, 1);
assert.equal(await key("b/", tree), "b/");
assert.equal(await key("b", tree), "b");
assert.equal(callCount, 1);
});
test("preserves trailing slashes", async () => {
const tree = new ObjectTree({
a: "letter a",
});
const addUnderscore = async (sourceKey) => `_${sourceKey}`;
const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
assert.equal(await key("a/", tree), "_a/");
assert.equal(await key("a", tree), "_a");
assert.equal(await inverseKey("_a/", tree), "a/");
assert.equal(await inverseKey("_a", tree), "a");
});
});

View File

@@ -0,0 +1,34 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import FunctionTree from "../../src/drivers/FunctionTree.js";
import { Tree } from "../../src/internal.js";
import concat from "../../src/operations/concat.js";
describe("concat", () => {
test("concatenates deep tree values", async () => {
const tree = Tree.from({
a: "A",
b: "B",
c: "C",
more: {
d: "D",
e: "E",
},
});
const result = await concat.call(null, tree);
assert.equal(result, "ABCDE");
});
test("concatenates deep tree-like values", async () => {
const letters = ["a", "b", "c"];
const specimens = new FunctionTree(
(letter) => ({
lowercase: letter,
uppercase: letter.toUpperCase(),
}),
letters
);
const result = await concat.call(null, specimens);
assert.equal(result, "aAbBcC");
});
});

View File

@@ -0,0 +1,42 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, Tree } from "../../src/internal.js";
import mergeDeep from "../../src/operations/deepMerge.js";
describe("mergeDeep", () => {
test("can merge deep", async () => {
const fixture = mergeDeep(
new DeepObjectTree({
a: {
b: 0, // Will be obscured by `b` below
c: {
d: 2,
},
},
}),
new DeepObjectTree({
a: {
b: 1,
c: {
e: 3,
},
f: 4,
},
})
);
assert.deepEqual(await Tree.plain(fixture), {
a: {
b: 1,
c: {
d: 2,
e: 3,
},
f: 4,
},
});
// Parent of a subvalue is the merged tree
const a = await fixture.get("a");
assert.equal(a.parent, fixture);
});
});

View File

@@ -0,0 +1,24 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import deepReverse from "../../src/operations/deepReverse.js";
describe("deepReverse", () => {
test("reverses keys at all levels of a tree", async () => {
const tree = {
a: 1,
b: {
c: 2,
d: 3,
},
};
const reversed = deepReverse.call(null, tree);
assert.deepEqual(await Tree.plain(reversed), {
b: {
d: 3,
c: 2,
},
a: 1,
});
});
});

View File

@@ -0,0 +1,22 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import deepTake from "../../src/operations/deepTake.js";
describe("deepTake", () => {
test("traverses deeply and returns a limited number of items", async () => {
const tree = {
a: 1,
b: {
c: 2,
d: {
e: 3,
},
f: 4,
},
g: 5,
};
const result = await deepTake(tree, 4);
assert.deepEqual(await Tree.plain(result), [1, 2, 3, 4]);
});
});

View File

@@ -0,0 +1,28 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import deepValues from "../../src/operations/deepValues.js";
describe("deepValues", () => {
test("returns in-order array of a tree's values", async () => {
const tree = {
a: 1,
b: {
c: 2,
d: {
e: 3,
},
},
f: {
g: 4,
},
};
const values = await deepValues(tree);
assert.deepEqual(values, [1, 2, 3, 4]);
});
test("returns in-order array of values in nested arrays", async () => {
const tree = [1, [2, 3], 4];
const values = await deepValues(tree);
assert.deepEqual(values, [1, 2, 3, 4]);
});
});

View File

@@ -0,0 +1,23 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { ObjectTree } from "../../src/internal.js";
import deepValuesIterator from "../../src/operations/deepValuesIterator.js";
describe("deepValuesIterator", () => {
test("returns an iterator of a tree's deep values", async () => {
const tree = new ObjectTree({
a: 1,
b: 2,
more: {
c: 3,
d: 4,
},
});
const values = [];
// The tree will be shallow, but we'll ask to expand the values.
for await (const value of deepValuesIterator(tree, { expand: true })) {
values.push(value);
}
assert.deepEqual(values, [1, 2, 3, 4]);
});
});

View File

@@ -0,0 +1,54 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import group from "../../src/operations/group.js";
describe("group transform", () => {
test("groups an array using a group key function", async () => {
const fonts = [
{ name: "Aboreto", tags: ["Sans Serif"] },
{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] },
{ name: "Alegreya", tags: ["Serif"] },
{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
];
const tree = Tree.from(fonts);
const grouped = await group(tree, (value, key, tree) => value.tags);
assert.deepEqual(await Tree.plain(grouped), {
Geometric: [{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] }],
Grotesque: [{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] }],
"Sans Serif": [
{ name: "Aboreto", tags: ["Sans Serif"] },
{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] },
{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
],
Serif: [{ name: "Alegreya", tags: ["Serif"] }],
});
});
test("groups an object using a group key function", async () => {
const fonts = {
Aboreto: { tags: ["Sans Serif"] },
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
Alegreya: { tags: ["Serif"] },
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
};
const tree = Tree.from(fonts);
const grouped = await group(tree, (value, key, tree) => value.tags);
assert.deepEqual(await Tree.plain(grouped), {
Geometric: {
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
},
Grotesque: {
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
},
"Sans Serif": {
Aboreto: { tags: ["Sans Serif"] },
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
},
Serif: {
Alegreya: { tags: ["Serif"] },
},
});
});
});

View File

@@ -0,0 +1,17 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import invokeFunctions from "../../src/operations/invokeFunctions.js";
describe("invokeFunctions", () => {
test("invokes function values, leaves other values as is", async () => {
const fixture = invokeFunctions({
a: 1,
b: () => 2,
});
assert.deepEqual(await Tree.plain(fixture), {
a: 1,
b: 2,
});
});
});

View File

@@ -0,0 +1,82 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { ObjectTree, Tree } from "../../src/internal.js";
import keyFunctionsForExtensions from "../../src/operations/keyFunctionsForExtensions.js";
import map from "../../src/operations/map.js";
describe("keyMapsForExtensions", () => {
test("returns key functions that pass a matching key through", async () => {
const { inverseKey, key } = keyFunctionsForExtensions({
sourceExtension: ".txt",
});
assert.equal(await inverseKey("file.txt"), "file.txt");
assert.equal(await inverseKey("file.txt/"), "file.txt");
assert.equal(await key("file.txt"), "file.txt");
assert.equal(await key("file.txt/"), "file.txt/");
assert.equal(await inverseKey("file.foo"), undefined);
assert.equal(await key("file.foo"), undefined);
});
test("returns key functions that can map extensions", async () => {
const { inverseKey, key } = keyFunctionsForExtensions({
resultExtension: ".json",
sourceExtension: ".md",
});
assert.equal(await inverseKey("file.json"), "file.md");
assert.equal(await inverseKey("file.json/"), "file.md");
assert.equal(await key("file.md"), "file.json");
assert.equal(await key("file.md/"), "file.json/");
assert.equal(await inverseKey("file.foo"), undefined);
assert.equal(await key("file.foo"), undefined);
});
test("key functions can handle a slash as an explicit extension", async () => {
const { inverseKey, key } = keyFunctionsForExtensions({
resultExtension: ".html",
sourceExtension: "/",
});
assert.equal(await inverseKey("file.html"), "file/");
assert.equal(await inverseKey("file.html/"), "file/");
assert.equal(await key("file"), undefined);
assert.equal(await key("file/"), "file.html");
});
test("works with map to handle keys that end in a given resultExtension", async () => {
const files = new ObjectTree({
"file1.txt": "will be mapped",
file2: "won't be mapped",
"file3.foo": "won't be mapped",
});
const { inverseKey, key } = keyFunctionsForExtensions({
sourceExtension: ".txt",
});
const fixture = map(files, {
inverseKey,
key,
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
});
assert.deepEqual(await Tree.plain(fixture), {
"file1.txt": "WILL BE MAPPED",
});
});
test("works with map to change a key's resultExtension", async () => {
const files = new ObjectTree({
"file1.txt": "will be mapped",
file2: "won't be mapped",
"file3.foo": "won't be mapped",
});
const { inverseKey, key } = keyFunctionsForExtensions({
resultExtension: ".upper",
sourceExtension: ".txt",
});
const fixture = map(files, {
inverseKey,
key,
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
});
assert.deepEqual(await Tree.plain(fixture), {
"file1.upper": "WILL BE MAPPED",
});
});
});

View File

@@ -0,0 +1,204 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import FunctionTree from "../../src/drivers/FunctionTree.js";
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
import map from "../../src/operations/map.js";
import * as trailingSlash from "../../src/trailingSlash.js";
describe("map", () => {
test("returns identity graph if no key or value function is supplied", async () => {
const tree = {
a: "letter a",
b: "letter b",
};
const mapped = map(tree, {});
assert.deepEqual(await Tree.plain(mapped), {
a: "letter a",
b: "letter b",
});
});
test("maps values", async () => {
const tree = new ObjectTree({
a: "letter a",
b: "letter b",
c: undefined, // Won't be mapped
});
const mapped = map(tree, {
value: (sourceValue, sourceKey, innerTree) => {
assert(sourceKey === "a" || sourceKey === "b");
assert.equal(innerTree, tree);
return sourceValue.toUpperCase();
},
});
assert.deepEqual(await Tree.plain(mapped), {
a: "LETTER A",
b: "LETTER B",
c: undefined,
});
});
test("interprets a single function argument as the value function", async () => {
const tree = {
a: "letter a",
b: "letter b",
};
const uppercaseValues = map(tree, (sourceValue, sourceKey, tree) => {
assert(sourceKey === "a" || sourceKey === "b");
return sourceValue.toUpperCase();
});
assert.deepEqual(await Tree.plain(uppercaseValues), {
a: "LETTER A",
b: "LETTER B",
});
});
test("maps keys using key and inverseKey", async () => {
const tree = {
a: "letter a",
b: "letter b",
};
const underscoreKeys = map(tree, {
key: addUnderscore,
inverseKey: removeUnderscore,
});
assert.deepEqual(await Tree.plain(underscoreKeys), {
_a: "letter a",
_b: "letter b",
});
});
test("maps keys and values", async () => {
const tree = {
a: "letter a",
b: "letter b",
};
const underscoreKeysUppercaseValues = map(tree, {
key: addUnderscore,
inverseKey: removeUnderscore,
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
});
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
_a: "LETTER A",
_b: "LETTER B",
});
});
test("a shallow map is applied to async subtrees too", async () => {
const tree = {
a: "letter a",
more: {
b: "letter b",
},
};
const underscoreKeys = map(tree, {
key: async (sourceKey, tree) => `_${sourceKey}`,
inverseKey: async (resultKey, tree) => resultKey.slice(1),
value: async (sourceValue, sourceKey, tree) => sourceKey,
});
assert.deepEqual(await Tree.plain(underscoreKeys), {
_a: "a",
_more: "more",
});
});
test("value can provide a default key and inverse key functions", async () => {
const uppercase = (s) => s.toUpperCase();
uppercase.key = addUnderscore;
uppercase.inverseKey = removeUnderscore;
const tree = {
a: "letter a",
b: "letter b",
};
const mapped = map(tree, uppercase);
assert.deepEqual(await Tree.plain(mapped), {
_a: "LETTER A",
_b: "LETTER B",
});
});
test("deep maps values", async () => {
const tree = new DeepObjectTree({
a: "letter a",
more: {
b: "letter b",
},
});
const uppercaseValues = map(tree, {
deep: true,
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
});
assert.deepEqual(await Tree.plain(uppercaseValues), {
a: "LETTER A",
more: {
b: "LETTER B",
},
});
});
test("deep maps leaf keys", async () => {
const tree = new DeepObjectTree({
a: "letter a",
more: {
b: "letter b",
},
});
const underscoreKeys = map(tree, {
deep: true,
key: addUnderscore,
inverseKey: removeUnderscore,
});
assert.deepEqual(await Tree.plain(underscoreKeys), {
_a: "letter a",
more: {
_b: "letter b",
},
});
});
test("deep maps leaf keys and values", async () => {
const tree = new DeepObjectTree({
a: "letter a",
more: {
b: "letter b",
},
});
const underscoreKeysUppercaseValues = map(tree, {
deep: true,
key: addUnderscore,
inverseKey: removeUnderscore,
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
});
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
_a: "LETTER A",
more: {
_b: "LETTER B",
},
});
});
test("needsSourceValue can be set to false in cases where the value isn't necessary", async () => {
let flag = false;
const tree = new FunctionTree(() => {
flag = true;
}, ["a", "b", "c"]);
const mapped = map(tree, {
needsSourceValue: false,
value: () => "X",
});
assert.deepEqual(await Tree.plain(mapped), {
a: "X",
b: "X",
c: "X",
});
assert(!flag);
});
});
function addUnderscore(key) {
return `_${key}`;
}
function removeUnderscore(key) {
return trailingSlash.has(key) ? key : key.slice(1);
}

View File

@@ -0,0 +1,64 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
import merge from "../../src/operations/merge.js";
import * as symbols from "../../src/symbols.js";
describe("merge", () => {
test("performs a shallow merge", async () => {
const fixture = merge(
{
a: 1,
// Will be obscured by `b` that follows
b: {
c: 2,
},
},
{
b: {
d: 3,
},
e: {
f: 4,
},
}
);
assert.deepEqual(await Tree.plain(fixture), {
a: 1,
b: {
d: 3,
},
e: {
f: 4,
},
});
// Merge is shallow, and last tree wins, so `b/c` doesn't exist
const c = await Tree.traverse(fixture, "b", "c");
assert.equal(c, undefined);
// Parent of a subvalue is the merged tree
const b = await fixture.get("b");
assert.equal(b[symbols.parent], fixture);
});
test("subtree can overwrite a leaf node", async () => {
const fixture = merge(
new ObjectTree({
a: 1,
}),
new DeepObjectTree({
a: {
b: 2,
},
})
);
assert.deepEqual([...(await fixture.keys())], ["a/"]);
assert.deepEqual(await Tree.plain(fixture), {
a: {
b: 2,
},
});
});
});

View File

@@ -0,0 +1,25 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, Tree } from "../../src/internal.js";
import regExpKeys from "../../src/operations/regExpKeys.js";
describe("regExpKeys", () => {
test("matches keys using regular expressions", async () => {
const fixture = await regExpKeys(
new DeepObjectTree({
a: true,
"b.*": true,
c: {
d: true,
"e*": true,
},
})
);
assert(await Tree.traverse(fixture, "a"));
assert(!(await Tree.traverse(fixture, "alice")));
assert(await Tree.traverse(fixture, "bob"));
assert(await Tree.traverse(fixture, "brenda"));
assert(await Tree.traverse(fixture, "c", "d"));
assert(await Tree.traverse(fixture, "c", "eee"));
});
});

View File

@@ -0,0 +1,23 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import reverse from "../../src/operations/reverse.js";
describe("reverse", () => {
test("reverses a tree's top-level keys", async () => {
const tree = {
a: "A",
b: "B",
c: "C",
};
const reversed = reverse.call(null, tree);
// @ts-ignore
assert.deepEqual(Array.from(await reversed.keys()), ["c", "b", "a"]);
// @ts-ignore
assert.deepEqual(await Tree.plain(reversed), {
c: "C",
b: "B",
a: "A",
});
});
});

View File

@@ -0,0 +1,25 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { ObjectTree } from "../../src/internal.js";
import scope from "../../src/operations/scope.js";
describe("scope", () => {
test("gets the first defined value from the scope trees", async () => {
const outer = new ObjectTree({
a: 1,
b: 2,
});
const inner = new ObjectTree({
a: 3,
});
inner.parent = outer;
const innerScope = scope(inner);
assert.deepEqual([...(await innerScope.keys())], ["a", "b"]);
// Inner tree has precedence
assert.equal(await innerScope.get("a"), 3);
// If tree doesn't have value, finds value from parent
assert.equal(await innerScope.get("b"), 2);
assert.equal(await innerScope.get("c"), undefined);
assert.deepEqual(innerScope.trees, [inner, outer]);
});
});

View File

@@ -0,0 +1,48 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import sort from "../../src/operations/sort.js";
describe("sort", () => {
test("sorts keys using default sort order", async () => {
const tree = Tree.from({
file10: null,
file1: null,
file9: null,
});
const sorted = sort(tree);
assert.deepEqual(Array.from(await sorted.keys()), [
"file1",
"file10",
"file9",
]);
});
test("invokes a comparison function", async () => {
const tree = Tree.from({
b: 2,
c: 3,
a: 1,
});
// Reverse order
const compare = (a, b) => (a > b ? -1 : a < b ? 1 : 0);
const sorted = sort(tree, { compare });
assert.deepEqual(Array.from(await sorted.keys()), ["c", "b", "a"]);
});
test("invokes a sortKey function", async () => {
const tree = {
Alice: { age: 48 },
Bob: { age: 36 },
Carol: { age: 42 },
};
const sorted = await sort(tree, {
sortKey: async (key, tree) => Tree.traverse(tree, key, "age"),
});
assert.deepEqual(Array.from(await sorted.keys()), [
"Bob",
"Carol",
"Alice",
]);
});
});

View File

@@ -0,0 +1,20 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { Tree } from "../../src/internal.js";
import take from "../../src/operations/take.js";
describe("take", () => {
test("limits the number of keys to the indicated count", async () => {
const tree = {
a: 1,
b: 2,
c: 3,
d: 4,
};
const result = await take(tree, 2);
assert.deepEqual(await Tree.plain(result), {
a: 1,
b: 2,
});
});
});