@weborigami/async-tree 0.7.0-beta.1 → 0.7.0-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.7.0-beta.1",
3
+ "version": "0.7.0-beta.2",
4
4
  "description": "Asynchronous tree drivers based on standard JavaScript classes",
5
5
  "type": "module",
6
6
  "main": "./main.js",
package/src/Tree.js CHANGED
@@ -25,6 +25,8 @@ export { default as deflatePaths } from "./operations/deflatePaths.js";
25
25
  export { default as delete } from "./operations/delete.js";
26
26
  export { default as entries } from "./operations/entries.js";
27
27
  export { default as filter } from "./operations/filter.js";
28
+ export { default as find } from "./operations/find.js";
29
+ export { default as findKey } from "./operations/findKey.js";
28
30
  export { default as first } from "./operations/first.js";
29
31
  export { default as flat } from "./operations/flat.js";
30
32
  export { default as forEach } from "./operations/forEach.js";
@@ -28,6 +28,7 @@ export default class AsyncMap {
28
28
  if (!isMap(result)) {
29
29
  // Create new child node using no-arg constructor
30
30
  result = new /** @type {any} */ (this.constructor)();
31
+ result.trailingSlashKeys = this.trailingSlashKeys;
31
32
  await this.set(key, result);
32
33
  }
33
34
 
@@ -49,6 +49,7 @@ export default class SyncMap extends Map {
49
49
  if (!(result instanceof Map)) {
50
50
  // Create new child node using no-arg constructor
51
51
  result = new /** @type {any} */ (this.constructor)();
52
+ result.trailingSlashKeys = this.trailingSlashKeys;
52
53
  this.set(key, result);
53
54
  }
54
55
 
@@ -0,0 +1,21 @@
1
+ import * as args from "../utilities/args.js";
2
+
3
+ /**
4
+ * Return the first value in the map that satisfies the given predicate
5
+ * function.
6
+ *
7
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
+ *
9
+ * @param {Maplike} maplike
10
+ * @param {Function} predicate
11
+ */
12
+ export default async function find(maplike, predicate) {
13
+ const map = await args.map(maplike, "Tree.find");
14
+ for await (const key of map.keys()) {
15
+ const value = await map.get(key);
16
+ if (await predicate(value, key, map)) {
17
+ return value;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
@@ -0,0 +1,21 @@
1
+ import * as args from "../utilities/args.js";
2
+
3
+ /**
4
+ * Return the first key in the map that satisfies the given predicate
5
+ * function.
6
+ *
7
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
+ *
9
+ * @param {Maplike} maplike
10
+ * @param {Function} predicate
11
+ */
12
+ export default async function findKey(maplike, predicate) {
13
+ const map = await args.map(maplike, "Tree.findKey");
14
+ for await (const key of map.keys()) {
15
+ const value = await map.get(key);
16
+ if (await predicate(value, key, map)) {
17
+ return key;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
@@ -2,13 +2,16 @@ import AsyncMap from "../drivers/AsyncMap.js";
2
2
  import FunctionMap from "../drivers/FunctionMap.js";
3
3
  import ObjectMap from "../drivers/ObjectMap.js";
4
4
  import SetMap from "../drivers/SetMap.js";
5
+ import SyncMap from "../drivers/SyncMap.js";
5
6
  import * as symbols from "../symbols.js";
6
7
  import box from "../utilities/box.js";
7
- import isPlainObject from "../utilities/isPlainObject.js";
8
8
  import setParent from "../utilities/setParent.js";
9
9
  import TypedArray from "../utilities/TypedArray.js";
10
10
  import isMap from "./isMap.js";
11
11
 
12
+ // Base class for async functions
13
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
14
+
12
15
  /**
13
16
  * Attempts to cast the indicated object to a map.
14
17
  *
@@ -42,15 +45,17 @@ export default function from(object, options = {}) {
42
45
  map = new SetMap(object);
43
46
  } else if (object instanceof ArrayBuffer || object instanceof TypedArray) {
44
47
  throw new TypeError("Can't treat binary file data as a map.");
45
- } else if (isPlainObject(object) || object instanceof Array) {
46
- map = new ObjectMap(object, { deep });
47
48
  // @ts-ignore
48
49
  } else if (globalThis.Iterator && object instanceof Iterator) {
49
50
  const array = Array.from(object);
50
51
  map = new ObjectMap(array, { deep });
51
- } else if (object && typeof object === "object") {
52
- // An instance of some class.
53
- map = new ObjectMap(object, { deep });
52
+ } else if (typeof object === "object") {
53
+ if (typeof object.get === "function" && typeof object.keys === "function") {
54
+ map = upgradeToMap(object);
55
+ } else {
56
+ // A plain object, array, or instance of some class
57
+ map = new ObjectMap(object, { deep });
58
+ }
54
59
  } else if (
55
60
  typeof object === "string" ||
56
61
  typeof object === "number" ||
@@ -69,3 +74,10 @@ export default function from(object, options = {}) {
69
74
 
70
75
  return map;
71
76
  }
77
+
78
+ function upgradeToMap(object) {
79
+ const isAsync =
80
+ object.get instanceof AsyncFunction || object.keys instanceof AsyncFunction;
81
+ const baseClass = isAsync ? AsyncMap : SyncMap;
82
+ return Object.assign(new baseClass(), object);
83
+ }
@@ -5,23 +5,24 @@ import keysFromPath from "../utilities/keysFromPath.js";
5
5
  /**
6
6
  * Given a mapping of string paths to values, return the described tree.
7
7
  */
8
- export default async function inflatePaths(maplike) {
9
- const map = await args.map(maplike, "Tree.inflatePaths", { deep: true });
8
+ export default async function inflatePaths(maplike, options = {}) {
9
+ const map = await args.map(maplike, "Tree.inflatePaths");
10
10
 
11
- const result = new SyncMap();
11
+ const classFn = options.classFn ?? SyncMap;
12
+ const result = new classFn();
12
13
  for await (const [path, value] of map) {
13
14
  const keys = keysFromPath(path);
14
- setValue(result, keys, value);
15
+ setValue(result, keys, value, classFn);
15
16
  }
16
17
  return result;
17
18
  }
18
19
 
19
20
  // Add the value to the tree at the given path of keys
20
- function setValue(map, keys, value) {
21
+ function setValue(map, keys, value, classFn) {
21
22
  let node = map;
22
23
  for (const key of keys.slice(0, -1)) {
23
24
  // Create a new node if one doesn't exist yet
24
- node = node.getOrInsertComputed(key, () => new SyncMap());
25
+ node = node.getOrInsertComputed(key, () => new classFn());
25
26
  }
26
27
  node.set(keys[keys.length - 1], value);
27
28
  }
@@ -0,0 +1,10 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import find from "../../src/operations/find.js";
4
+
5
+ describe("find", () => {
6
+ test("returns the first value that satisfies the predicate", async () => {
7
+ const result = await find([5, 12, 8], (e) => e > 10);
8
+ assert.strictEqual(result, 12);
9
+ });
10
+ });
@@ -0,0 +1,15 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import findKey from "../../src/operations/findKey.js";
4
+
5
+ describe("findKey", () => {
6
+ test("returns the first key that satisfies the predicate", async () => {
7
+ const object = {
8
+ a: 5,
9
+ b: 12,
10
+ c: 8,
11
+ };
12
+ const result = await findKey(object, (e) => e > 10);
13
+ assert.strictEqual(result, "b");
14
+ });
15
+ });
@@ -58,4 +58,22 @@ describe("from", () => {
58
58
  const result = await slice(0, 5);
59
59
  assert.equal(result, "Hello");
60
60
  });
61
+
62
+ test("upgrades object with get/keys to a Map", () => {
63
+ const object = {
64
+ get(key) {
65
+ return key.toUpperCase();
66
+ },
67
+
68
+ *keys() {
69
+ yield "a";
70
+ yield "b";
71
+ },
72
+ };
73
+ const tree = from(object);
74
+ // @ts-ignore
75
+ assert.deepEqual([...tree.keys()], ["a", "b"]);
76
+ assert.equal(tree.get("a"), "A");
77
+ assert.equal(tree.get("b"), "B");
78
+ });
61
79
  });