@weborigami/async-tree 0.0.64-beta.2 → 0.0.65-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/browser.js CHANGED
@@ -4,10 +4,10 @@ export { default as DeferredTree } from "./src/DeferredTree.js";
4
4
  // Skip FileTree.js, which is Node.js only.
5
5
  export { default as BrowserFileTree } from "./src/BrowserFileTree.js";
6
6
  export { default as FunctionTree } from "./src/FunctionTree.js";
7
+ export * as keysJson from "./src/jsonKeys.js";
7
8
  export { default as MapTree } from "./src/MapTree.js";
8
9
  export { default as ObjectTree } from "./src/ObjectTree.js";
9
10
  export { default as SetTree } from "./src/SetTree.js";
10
11
  export { default as SiteTree } from "./src/SiteTree.js";
11
12
  export * as Tree from "./src/Tree.js";
12
- export * as keysJson from "./src/keysJson.js";
13
13
  export * from "./src/utilities.js";
package/main.js CHANGED
@@ -6,10 +6,8 @@ export { default as FunctionTree } from "./src/FunctionTree.js";
6
6
  export { default as MapTree } from "./src/MapTree.js";
7
7
  // Skip BrowserFileTree.js, which is browser-only.
8
8
  export { default as DeepMapTree } from "./src/DeepMapTree.js";
9
- export { default as SetTree } from "./src/SetTree.js";
10
- export { default as SiteTree } from "./src/SiteTree.js";
11
9
  export { DeepObjectTree, ObjectTree, Tree } from "./src/internal.js";
12
- export * as keysJson from "./src/keysJson.js";
10
+ export * as jsonKeys from "./src/jsonKeys.js";
13
11
  export { default as cache } from "./src/operations/cache.js";
14
12
  export { default as concat } from "./src/operations/concat.js";
15
13
  export { default as deepMerge } from "./src/operations/deepMerge.js";
@@ -24,6 +22,8 @@ export { default as merge } from "./src/operations/merge.js";
24
22
  export { default as scope } from "./src/operations/scope.js";
25
23
  export { default as sort } from "./src/operations/sort.js";
26
24
  export { default as take } from "./src/operations/take.js";
25
+ export { default as SetTree } from "./src/SetTree.js";
26
+ export { default as SiteTree } from "./src/SiteTree.js";
27
27
  export * as symbols from "./src/symbols.js";
28
28
  export { default as cachedKeyFunctions } from "./src/transforms/cachedKeyFunctions.js";
29
29
  export { default as deepReverse } from "./src/transforms/deepReverse.js";
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.64-beta.2",
3
+ "version": "0.0.65-beta.1",
4
4
  "description": "Asynchronous tree drivers based on standard JavaScript classes",
5
5
  "type": "module",
6
6
  "main": "./main.js",
7
7
  "browser": "./browser.js",
8
8
  "types": "./index.ts",
9
9
  "devDependencies": {
10
- "@types/node": "20.14.9",
11
- "typescript": "5.5.3"
10
+ "@types/node": "22.5.4",
11
+ "typescript": "5.5.4"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.0.64-beta.2"
14
+ "@weborigami/types": "0.0.65-beta.1"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
@@ -34,6 +34,13 @@ export default class BrowserFileTree {
34
34
  }
35
35
 
36
36
  async get(key) {
37
+ if (key == null) {
38
+ // Reject nullish key.
39
+ throw new ReferenceError(
40
+ `${this.constructor.name}: Cannot get a null or undefined key.`
41
+ );
42
+ }
43
+
37
44
  const directory = await this.getDirectory();
38
45
 
39
46
  // Try the key as a subfolder name.
package/src/FileTree.js CHANGED
@@ -49,9 +49,11 @@ export default class FileTree {
49
49
  }
50
50
 
51
51
  async get(key) {
52
- if (!key) {
53
- // Undefined key or empty string key is invalid.
54
- return undefined;
52
+ if (key == null) {
53
+ // Reject nullish key.
54
+ throw new ReferenceError(
55
+ `${this.constructor.name}: Cannot get a null or undefined key.`
56
+ );
55
57
  }
56
58
 
57
59
  const filePath = path.resolve(this.dirname, key);
@@ -27,11 +27,11 @@ export default class FunctionTree {
27
27
  this.fn.length <= 1
28
28
  ? // Function takes no arguments, one argument, or a variable number of
29
29
  // arguments: invoke it.
30
- await this.fn.call(null, key)
30
+ await this.fn.call(this.parent, key)
31
31
  : // Bind the key to the first parameter. Subsequent get calls will
32
32
  // eventually bind all parameters until only one remains. At that point,
33
33
  // the above condition will apply and the function will be invoked.
34
- Reflect.construct(this.constructor, [this.fn.bind(null, key)]);
34
+ Reflect.construct(this.constructor, [this.fn.bind(this.parent, key)]);
35
35
  setParent(value, this);
36
36
  return value;
37
37
  }
package/src/MapTree.js CHANGED
@@ -23,6 +23,13 @@ export default class MapTree {
23
23
  }
24
24
 
25
25
  async get(key) {
26
+ if (key == null) {
27
+ // Reject nullish key.
28
+ throw new ReferenceError(
29
+ `${this.constructor.name}: Cannot get a null or undefined key.`
30
+ );
31
+ }
32
+
26
33
  const value = this.map.get(key);
27
34
  setParent(value, this);
28
35
  return value;
package/src/ObjectTree.js CHANGED
@@ -25,15 +25,11 @@ export default class ObjectTree {
25
25
  * @param {any} key
26
26
  */
27
27
  async get(key) {
28
- // If the value is an array, we require that the key be one of its own
29
- // properties: we don't want to return Array prototype methods like `map`
30
- // and `find`.
31
- if (
32
- this.object instanceof Array &&
33
- key &&
34
- !this.object.hasOwnProperty(key)
35
- ) {
36
- return undefined;
28
+ if (key == null) {
29
+ // Reject nullish key.
30
+ throw new ReferenceError(
31
+ `${this.constructor.name}: Cannot get a null or undefined key.`
32
+ );
37
33
  }
38
34
 
39
35
  let value = await this.object[key];
package/src/SetTree.js CHANGED
@@ -17,6 +17,13 @@ export default class SetTree {
17
17
  }
18
18
 
19
19
  async get(key) {
20
+ if (key == null) {
21
+ // Reject nullish key.
22
+ throw new ReferenceError(
23
+ `${this.constructor.name}: Cannot get a null or undefined key.`
24
+ );
25
+ }
26
+
20
27
  const value = this.values[key];
21
28
  setParent(value, this);
22
29
  return value;
package/src/SiteTree.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "./internal.js";
2
- import * as keysJson from "./keysJson.js";
2
+ import * as jsonKeys from "./jsonKeys.js";
3
3
  import { setParent } from "./utilities.js";
4
4
 
5
5
  /**
@@ -31,6 +31,13 @@ export default class SiteTree {
31
31
  }
32
32
 
33
33
  async get(key) {
34
+ if (key == null) {
35
+ // Reject nullish key.
36
+ throw new ReferenceError(
37
+ `${this.constructor.name}: Cannot get a null or undefined key.`
38
+ );
39
+ }
40
+
34
41
  // If there is only one key and it's the empty string, and the site is
35
42
  // explorable, we take the route as "index.html". With this and subsequent
36
43
  // checks, we try to avoid sniffing the site to see if it's explorable, as
@@ -86,7 +93,7 @@ export default class SiteTree {
86
93
  .then((response) => (response.ok ? response.text() : null))
87
94
  .then((text) => {
88
95
  try {
89
- return text ? keysJson.parse(text) : null;
96
+ return text ? jsonKeys.parse(text) : null;
90
97
  } catch (error) {
91
98
  // Got a response, but it's not JSON. Most likely the site doesn't
92
99
  // actually have a .keys.json file, and is returning a Not Found page,
package/src/Tree.js CHANGED
@@ -402,13 +402,10 @@ export async function traverse(treelike, ...keys) {
402
402
  * @param {...any} keys
403
403
  */
404
404
  export async function traverseOrThrow(treelike, ...keys) {
405
- if (!treelike) {
406
- throw new TraverseError("Tried to traverse a null or undefined value");
407
- }
408
-
409
405
  // Start our traversal at the root of the tree.
410
406
  /** @type {any} */
411
407
  let value = treelike;
408
+ let position = 0;
412
409
 
413
410
  // If traversal operation was called with a `this` context, use that as the
414
411
  // target for function calls.
@@ -419,13 +416,11 @@ export async function traverseOrThrow(treelike, ...keys) {
419
416
  let key;
420
417
  while (remainingKeys.length > 0) {
421
418
  if (value === undefined) {
422
- // Attempted to traverse an undefined value
423
- const message = key
424
- ? `${key} does not exist`
425
- : `Couldn't traverse the path: ${keys
426
- .map((key) => String(key))
427
- .join("/")}`;
428
- throw new TraverseError(message, treelike, keys);
419
+ throw new TraverseError("Tried to traverse a null or undefined value", {
420
+ tree: treelike,
421
+ keys,
422
+ position,
423
+ });
429
424
  }
430
425
 
431
426
  // If the value is packed and can be unpacked, unpack it.
@@ -466,6 +461,8 @@ export async function traverseOrThrow(treelike, ...keys) {
466
461
  value = originalValue;
467
462
  }
468
463
  }
464
+
465
+ position++;
469
466
  }
470
467
 
471
468
  return value;
@@ -485,11 +482,10 @@ export async function traversePath(tree, path) {
485
482
 
486
483
  // Error class thrown by traverseOrThrow()
487
484
  class TraverseError extends ReferenceError {
488
- constructor(message, tree, keys) {
485
+ constructor(message, options) {
489
486
  super(message);
490
- this.tree = tree;
491
487
  this.name = "TraverseError";
492
- this.keys = keys;
488
+ Object.assign(this, options);
493
489
  }
494
490
  }
495
491
 
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Return a tree of years, months, and days from a start date to an end date.
3
+ *
4
+ * Both the start and end date can be provided in "YYYY-MM-DD", "YYYY-MM", or
5
+ * "YYYY" format. If a start year is provided, but a month is not, then the
6
+ * first month of the year will be used; if a start month is provided, but a day
7
+ * is not, then the first day of that month will be used. Similar logic applies
8
+ * to the end date, using the last month of the year or the last day of the
9
+ * month.
10
+ *
11
+ * If a start date is omitted, today will be used, likewise for the end date.
12
+ *
13
+ * @param {string} [start] - Start date in "YYYY-MM-DD", "YYYY-MM", or "YYYY"
14
+ * format
15
+ * @param {string} [end] - End date in "YYYY-MM-DD", "YYYY-MM", or "YYYY" format
16
+ */
17
+ export default function calendarTree(start, end) {
18
+ const startParts = start?.split("-") ?? [];
19
+ const endParts = end?.split("-") ?? [];
20
+
21
+ const today = new Date();
22
+
23
+ const startYear = startParts[0]
24
+ ? parseInt(startParts[0])
25
+ : today.getFullYear();
26
+ const startMonth = startParts[1]
27
+ ? parseInt(startParts[1])
28
+ : startParts[0]
29
+ ? 1
30
+ : today.getMonth() + 1;
31
+ const startDay = startParts[2]
32
+ ? parseInt(startParts[2])
33
+ : startParts[1]
34
+ ? 1
35
+ : today.getDate();
36
+
37
+ const endYear = endParts[0] ? parseInt(endParts[0]) : today.getFullYear();
38
+ const endMonth = endParts[1]
39
+ ? parseInt(endParts[1])
40
+ : endParts[0]
41
+ ? 12
42
+ : today.getMonth() + 1;
43
+ const endDay = endParts[2]
44
+ ? parseInt(endParts[2])
45
+ : endParts[1]
46
+ ? daysInMonth(endYear, endMonth)
47
+ : today.getDate();
48
+
49
+ let years = {};
50
+ for (let year = startYear; year <= endYear; year++) {
51
+ let months = new Map();
52
+ const firstMonth = year === startYear ? startMonth : 1;
53
+ const lastMonth = year === endYear ? endMonth : 12;
54
+ for (let month = firstMonth; month <= lastMonth; month++) {
55
+ const monthPadded = month.toString().padStart(2, "0");
56
+ let days = new Map();
57
+ const firstDay =
58
+ year === startYear && month === startMonth ? startDay : 1;
59
+ const lastDay =
60
+ year === endYear && month === endMonth
61
+ ? endDay
62
+ : daysInMonth(year, month);
63
+ for (let day = firstDay; day <= lastDay; day++) {
64
+ const dayPadded = day.toString().padStart(2, "0");
65
+ days.set(dayPadded, null);
66
+ }
67
+ months.set(monthPadded, days);
68
+ }
69
+ years[year] = months;
70
+ }
71
+
72
+ return years;
73
+ }
74
+
75
+ function daysInMonth(year, month) {
76
+ return new Date(year, month, 0).getDate();
77
+ }
@@ -1,8 +1,8 @@
1
1
  import { Tree } from "./internal.js";
2
2
 
3
3
  /**
4
- * The .keys.json file format lets a site expose the keys of a node in the site
5
- * so that they can be read by SiteTree.
4
+ * The JSON Keys protocol lets a site expose the keys of a node in the site so
5
+ * that they can be read by SiteTree.
6
6
  *
7
7
  * This file format is a JSON array of key descriptors: a string like
8
8
  * "index.html" for a specific resource available at the node, or a string with
@@ -1,4 +1,4 @@
1
- import { DeepObjectTree, Tree } from "../internal.js";
1
+ import { ObjectTree, Tree } from "../internal.js";
2
2
 
3
3
  /**
4
4
  * Caches values from a source tree in a second cache tree. Cache source tree
@@ -14,16 +14,30 @@ import { DeepObjectTree, Tree } from "../internal.js";
14
14
  * @typedef {import("../../index.ts").Treelike} Treelike
15
15
  *
16
16
  * @param {Treelike} sourceTreelike
17
- * @param {AsyncMutableTree} [cacheTree]
17
+ * @param {AsyncMutableTree} [cacheTreelike]
18
18
  * @param {Treelike} [filterTreelike]
19
19
  * @returns {AsyncTree & { description: string }}
20
20
  */
21
- export default function treeCache(sourceTreelike, cacheTree, filterTreelike) {
21
+ export default function treeCache(
22
+ sourceTreelike,
23
+ cacheTreelike,
24
+ filterTreelike
25
+ ) {
22
26
  const source = Tree.from(sourceTreelike);
23
27
  const filter = filterTreelike ? Tree.from(filterTreelike) : undefined;
24
28
 
25
29
  /** @type {AsyncMutableTree} */
26
- const cache = cacheTree ?? new DeepObjectTree({});
30
+ let cache;
31
+ if (cacheTreelike) {
32
+ // @ts-ignore
33
+ cache = Tree.from(cacheTreelike);
34
+ if (!Tree.isAsyncMutableTree(cache)) {
35
+ throw new Error("Cache tree must define a set() method.");
36
+ }
37
+ } else {
38
+ cache = new ObjectTree({});
39
+ }
40
+
27
41
  let keys;
28
42
  return {
29
43
  description: "cache",
@@ -47,8 +61,9 @@ export default function treeCache(sourceTreelike, cacheTree, filterTreelike) {
47
61
  // Construct merged tree for a tree result.
48
62
  if (cacheValue === undefined) {
49
63
  // Construct new container in cache
50
- await cache.set(key, {});
51
- cacheValue = await cache.get(key);
64
+ cacheValue = new ObjectTree({});
65
+ cacheValue.parent = this;
66
+ await cache.set(key, cacheValue);
52
67
  }
53
68
  value = treeCache(value, cacheValue, filterValue);
54
69
  } else {
@@ -30,6 +30,16 @@ if (isBrowser) {
30
30
  assert.equal(await fixture.get("xyz"), undefined);
31
31
  });
32
32
 
33
+ test("getting a null/undefined key throws an exception", async () => {
34
+ const fixture = await createFixture();
35
+ await assert.rejects(async () => {
36
+ await fixture.get(null);
37
+ });
38
+ await assert.rejects(async () => {
39
+ await fixture.get(undefined);
40
+ });
41
+ });
42
+
33
43
  test("can set a value", async () => {
34
44
  const fixture = await createFixture();
35
45
 
@@ -11,7 +11,7 @@ const tempDirectory = path.join(dirname, "fixtures/temp");
11
11
 
12
12
  const textDecoder = new TextDecoder();
13
13
 
14
- describe.only("FileTree", async () => {
14
+ describe("FileTree", async () => {
15
15
  test("can get the keys of the tree", async () => {
16
16
  const fixture = createFixture("fixtures/markdown");
17
17
  assert.deepEqual(Array.from(await fixture.keys()), [
@@ -33,6 +33,16 @@ describe.only("FileTree", async () => {
33
33
  assert.equal(await fixture.get("xyz"), undefined);
34
34
  });
35
35
 
36
+ test("getting a null/undefined key throws an exception", async () => {
37
+ const fixture = createFixture("fixtures/markdown");
38
+ await assert.rejects(async () => {
39
+ await fixture.get(null);
40
+ });
41
+ await assert.rejects(async () => {
42
+ await fixture.get(undefined);
43
+ });
44
+ });
45
+
36
46
  test("sets parent on subtrees", async () => {
37
47
  const fixture = createFixture("fixtures");
38
48
  const markdown = await fixture.get("markdown");
@@ -64,7 +74,7 @@ describe.only("FileTree", async () => {
64
74
  await removeTempDirectory();
65
75
  });
66
76
 
67
- test.only("can create empty subfolder via set()", async () => {
77
+ test("can create empty subfolder via set()", async () => {
68
78
  await createTempDirectory();
69
79
 
70
80
  // Write out new, empty folder called "empty".
@@ -26,6 +26,16 @@ describe("MapTree", () => {
26
26
  const fixture = createFixture();
27
27
  assert.equal(await fixture.get("d"), undefined);
28
28
  });
29
+
30
+ test("getting a null/undefined key throws an exception", async () => {
31
+ const fixture = createFixture();
32
+ await assert.rejects(async () => {
33
+ await fixture.get(null);
34
+ });
35
+ await assert.rejects(async () => {
36
+ await fixture.get(undefined);
37
+ });
38
+ });
29
39
  });
30
40
 
31
41
  function createFixture() {
@@ -24,6 +24,16 @@ describe("ObjectTree", () => {
24
24
  assert.equal(await fixture.get("xyz"), undefined);
25
25
  });
26
26
 
27
+ test("getting a null/undefined key throws an exception", async () => {
28
+ const fixture = createFixture();
29
+ await assert.rejects(async () => {
30
+ await fixture.get(null);
31
+ });
32
+ await assert.rejects(async () => {
33
+ await fixture.get(undefined);
34
+ });
35
+ });
36
+
27
37
  test("can set a value", async () => {
28
38
  const tree = new ObjectTree({
29
39
  a: 1,
@@ -23,6 +23,17 @@ describe("SetTree", () => {
23
23
  assert.equal(await fixture.get(3), undefined);
24
24
  });
25
25
 
26
+ test("getting a null/undefined key throws an exception", async () => {
27
+ const set = new Set(["a", "b", "c"]);
28
+ const fixture = new SetTree(set);
29
+ await assert.rejects(async () => {
30
+ await fixture.get(null);
31
+ });
32
+ await assert.rejects(async () => {
33
+ await fixture.get(undefined);
34
+ });
35
+ });
36
+
26
37
  test("sets parent on subtrees", async () => {
27
38
  const set = new Set();
28
39
  set.add(new ObjectTree({}));
@@ -66,6 +66,16 @@ describe("SiteTree", () => {
66
66
  assert.equal(await fixture.get("xyz"), undefined);
67
67
  });
68
68
 
69
+ test("getting a null/undefined key throws an exception", async () => {
70
+ const fixture = new SiteTree(mockHost);
71
+ await assert.rejects(async () => {
72
+ await fixture.get(null);
73
+ });
74
+ await assert.rejects(async () => {
75
+ await fixture.get(undefined);
76
+ });
77
+ });
78
+
69
79
  test("a redirect on a site with keys returns a SiteTree for the new URL", async () => {
70
80
  const fixture = new SiteTree(mockHost);
71
81
  const about = await fixture.get("about");
@@ -0,0 +1,111 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { toPlainValue } from "../main.js";
4
+ import calendarTree from "../src/calendarTree.js";
5
+
6
+ describe("calendarTree", () => {
7
+ test("without a start or end, returns a tree for today", async () => {
8
+ const tree = calendarTree();
9
+ const plain = await toPlainValue(tree);
10
+ const today = new Date();
11
+ const year = today.getFullYear();
12
+ const month = (today.getMonth() + 1).toString().padStart(2, "0");
13
+ const day = today.getDate().toString().padStart(2, "0");
14
+ assert.deepEqual(plain, {
15
+ [year]: {
16
+ [month]: {
17
+ [day]: null,
18
+ },
19
+ },
20
+ });
21
+ });
22
+
23
+ test("returns a tree for a month range", async () => {
24
+ const tree = calendarTree("2025-01", "2025-02");
25
+ const plain = await toPlainValue(tree);
26
+ assert.deepEqual(plain, {
27
+ 2025: {
28
+ "01": {
29
+ "01": null,
30
+ "02": null,
31
+ "03": null,
32
+ "04": null,
33
+ "05": null,
34
+ "06": null,
35
+ "07": null,
36
+ "08": null,
37
+ "09": null,
38
+ 10: null,
39
+ 11: null,
40
+ 12: null,
41
+ 13: null,
42
+ 14: null,
43
+ 15: null,
44
+ 16: null,
45
+ 17: null,
46
+ 18: null,
47
+ 19: null,
48
+ 20: null,
49
+ 21: null,
50
+ 22: null,
51
+ 23: null,
52
+ 24: null,
53
+ 25: null,
54
+ 26: null,
55
+ 27: null,
56
+ 28: null,
57
+ 29: null,
58
+ 30: null,
59
+ 31: null,
60
+ },
61
+ "02": {
62
+ "01": null,
63
+ "02": null,
64
+ "03": null,
65
+ "04": null,
66
+ "05": null,
67
+ "06": null,
68
+ "07": null,
69
+ "08": null,
70
+ "09": null,
71
+ 10: null,
72
+ 11: null,
73
+ 12: null,
74
+ 13: null,
75
+ 14: null,
76
+ 15: null,
77
+ 16: null,
78
+ 17: null,
79
+ 18: null,
80
+ 19: null,
81
+ 20: null,
82
+ 21: null,
83
+ 22: null,
84
+ 23: null,
85
+ 24: null,
86
+ 25: null,
87
+ 26: null,
88
+ 27: null,
89
+ 28: null,
90
+ },
91
+ },
92
+ });
93
+ });
94
+
95
+ test("returns a tree for a day range", async () => {
96
+ const tree = calendarTree("2025-02-27", "2025-03-02");
97
+ const plain = await toPlainValue(tree);
98
+ assert.deepEqual(plain, {
99
+ 2025: {
100
+ "02": {
101
+ 27: null,
102
+ 28: null,
103
+ },
104
+ "03": {
105
+ "01": null,
106
+ "02": null,
107
+ },
108
+ },
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,24 @@
1
+ import { DeepObjectTree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import * as jsonKeys from "../src/jsonKeys.js";
5
+
6
+ describe("jsonKeys", () => {
7
+ test("parses JSON Keys", async () => {
8
+ const json = '["index.html","about/"]';
9
+ const parsed = jsonKeys.parse(json);
10
+ assert.deepStrictEqual(parsed, {
11
+ "index.html": false,
12
+ about: true,
13
+ });
14
+ });
15
+
16
+ test("stringifies JSON Keys", async () => {
17
+ const tree = new DeepObjectTree({
18
+ about: {},
19
+ "index.html": "Home",
20
+ });
21
+ const json = await jsonKeys.stringify(tree);
22
+ assert.strictEqual(json, '["about/","index.html"]');
23
+ });
24
+ });
@@ -1,13 +1,13 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import { ObjectTree, Tree } from "../../src/internal.js";
3
+ import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
4
4
  import cache from "../../src/operations/cache.js";
5
5
 
6
6
  describe("cache", () => {
7
7
  test("caches reads of values from one tree into another", async () => {
8
8
  const objectCache = new ObjectTree({});
9
9
  const fixture = cache(
10
- Tree.from({
10
+ new DeepObjectTree({
11
11
  a: 1,
12
12
  b: 2,
13
13
  c: 3,
@@ -18,7 +18,7 @@ describe("cache", () => {
18
18
  objectCache
19
19
  );
20
20
 
21
- const keys = Array.from(await fixture.keys());
21
+ const keys = [...(await fixture.keys())];
22
22
  assert.deepEqual(keys, ["a", "b", "c", "more"]);
23
23
 
24
24
  assert.equal(await objectCache.get("a"), undefined);
@@ -28,6 +28,13 @@ describe("cache", () => {
28
28
  assert.equal(await objectCache.get("b"), undefined);
29
29
  assert.equal(await fixture.get("b"), 2);
30
30
  assert.equal(await objectCache.get("b"), 2);
31
+
32
+ assert.equal(await objectCache.get("more"), undefined);
33
+ const more = await fixture.get("more");
34
+ assert.deepEqual([...(await more.keys())], ["d"]);
35
+ assert.equal(await more.get("d"), 4);
36
+ const moreCache = await objectCache.get("more");
37
+ assert.equal(await moreCache.get("d"), 4);
31
38
  });
32
39
 
33
40
  test("if a cache filter is supplied, it only caches values whose keys match the filter", async () => {
File without changes