@weborigami/async-tree 0.0.47 → 0.0.49

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,17 +1,17 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.47",
3
+ "version": "0.0.49",
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.11.27",
11
- "typescript": "5.4.2"
10
+ "@types/node": "20.12.8",
11
+ "typescript": "5.4.5"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.0.47"
14
+ "@weborigami/types": "0.0.49"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
package/src/FileTree.js CHANGED
@@ -189,7 +189,7 @@ function isStringLike(obj) {
189
189
  return true;
190
190
  } else if (obj?.toString === undefined) {
191
191
  return false;
192
- } else if (obj.toString === getRealmObjectPrototype(obj).toString) {
192
+ } else if (obj.toString === getRealmObjectPrototype(obj)?.toString) {
193
193
  // The stupid Object.prototype.toString implementation always returns
194
194
  // "[object Object]", so if that's the only toString method the object has,
195
195
  // we return false.
package/src/Tree.js CHANGED
@@ -388,11 +388,16 @@ export async function traverseOrThrow(treelike, ...keys) {
388
388
  value = await value.unpack();
389
389
  }
390
390
 
391
- // Peek ahead: if there's only one key left and it's an empty string, return
392
- // the value itself.
393
- if (remainingKeys.length === 1 && remainingKeys[0] === "") {
394
- return value;
395
- } else if (value instanceof Function) {
391
+ if (!isTreelike(value)) {
392
+ // Value isn't treelike, so can't be traversed except for a special case:
393
+ // if there's only one key left and it's an empty string, return the
394
+ // non-treelike value.
395
+ if (remainingKeys.length === 1 && remainingKeys[0] === "") {
396
+ return value;
397
+ }
398
+ }
399
+
400
+ if (value instanceof Function) {
396
401
  // Value is a function: call it with the remaining keys.
397
402
  const fn = value;
398
403
  // We'll take as many keys as the function's length, but at least one.
@@ -401,12 +406,20 @@ export async function traverseOrThrow(treelike, ...keys) {
401
406
  key = null;
402
407
  value = await fn.call(target, ...args);
403
408
  } else {
409
+ const originalValue = value;
410
+
404
411
  // Value is some other treelike object: cast it to a tree.
405
412
  const tree = from(value);
406
413
  // Get the next key.
407
414
  key = remainingKeys.shift();
408
415
  // Get the value for the key.
409
416
  value = await tree.get(key);
417
+
418
+ // The empty key as the final key is a special case: if the tree doesn't
419
+ // have a value for the empty key, use the original value.
420
+ if (value === undefined && remainingKeys.length === 0 && key === "") {
421
+ value = originalValue;
422
+ }
410
423
  }
411
424
  }
412
425
 
@@ -1,3 +1,5 @@
1
+ import { Tree } from "../internal.js";
2
+
1
3
  /**
2
4
  * Return a tree that performs a shallow merge of the given trees.
3
5
  *
@@ -8,10 +10,11 @@
8
10
  * return a defined value, the `get` method returns undefined.
9
11
  *
10
12
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
13
+ * @param {import("../../index.ts").Treelike[]} sources
11
14
  * @returns {AsyncTree & { description: string }}
12
15
  */
13
16
  export default function merge(...sources) {
14
- let trees = sources;
17
+ let trees = sources.map((treelike) => Tree.from(treelike));
15
18
  let mergeParent;
16
19
  return {
17
20
  description: "merge",
@@ -28,7 +31,7 @@ export default function merge(...sources) {
28
31
 
29
32
  async isKeyForSubtree(key) {
30
33
  for (const tree of trees) {
31
- if (await tree.isKeyForSubtree(key)) {
34
+ if (await Tree.isKeyForSubtree(tree, key)) {
32
35
  return true;
33
36
  }
34
37
  }
@@ -50,8 +53,10 @@ export default function merge(...sources) {
50
53
  },
51
54
  set parent(parent) {
52
55
  mergeParent = parent;
53
- trees = sources.map((source) => {
54
- const tree = Object.create(source);
56
+ trees = sources.map((treelike) => {
57
+ const tree = Tree.isAsyncTree(treelike)
58
+ ? Object.create(treelike)
59
+ : Tree.from(treelike);
55
60
  tree.parent = parent;
56
61
  return tree;
57
62
  });
@@ -4,9 +4,12 @@ import { Tree } from "../internal.js";
4
4
  * Return a tree that performs a deep merge of the given trees.
5
5
  *
6
6
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
7
+ * @param {import("../../index.ts").Treelike[]} sources
7
8
  * @returns {AsyncTree & { description: string }}
8
9
  */
9
- export default function mergeDeep(...trees) {
10
+ export default function mergeDeep(...sources) {
11
+ let trees = sources.map((treelike) => Tree.from(treelike));
12
+ let mergeParent;
10
13
  return {
11
14
  description: "mergeDeep",
12
15
 
@@ -27,7 +30,7 @@ export default function mergeDeep(...trees) {
27
30
 
28
31
  async isKeyForSubtree(key) {
29
32
  for (const tree of trees) {
30
- if (await tree.isKeyForSubtree(key)) {
33
+ if (await Tree.isKeyForSubtree(tree, key)) {
31
34
  return true;
32
35
  }
33
36
  }
@@ -43,5 +46,19 @@ export default function mergeDeep(...trees) {
43
46
  }
44
47
  return keys;
45
48
  },
49
+
50
+ get parent() {
51
+ return mergeParent;
52
+ },
53
+ set parent(parent) {
54
+ mergeParent = parent;
55
+ trees = sources.map((treelike) => {
56
+ const tree = Tree.isAsyncTree(treelike)
57
+ ? Object.create(treelike)
58
+ : Tree.from(treelike);
59
+ tree.parent = parent;
60
+ return tree;
61
+ });
62
+ },
46
63
  };
47
64
  }
@@ -1,4 +1,4 @@
1
- import { DeepObjectTree, Tree } from "../internal.js";
1
+ import { ObjectTree, Tree } from "../internal.js";
2
2
 
3
3
  /**
4
4
  * Given a function that returns a grouping key for a value, returns a transform
@@ -36,6 +36,6 @@ export default function createGroupByTransform(groupKeyFn) {
36
36
  }
37
37
  }
38
38
 
39
- return new DeepObjectTree(result);
39
+ return new ObjectTree(result);
40
40
  };
41
41
  }
@@ -1,4 +1,5 @@
1
- import { Tree } from "../internal.js";
1
+ import { ObjectTree, Tree } from "../internal.js";
2
+ import { isPlainObject } from "../utilities.js";
2
3
 
3
4
  /**
4
5
  * Return a transform function that maps the keys and/or values of a tree.
@@ -45,7 +46,11 @@ export default function createMapTransform(options = {}) {
45
46
  * @type {import("../../index.ts").TreeTransform}
46
47
  */
47
48
  return function map(treelike) {
48
- const tree = Tree.from(treelike);
49
+ const tree =
50
+ !deep && isPlainObject(treelike) && !Tree.isAsyncTree(treelike)
51
+ ? new ObjectTree(treelike)
52
+ : Tree.from(treelike);
53
+
49
54
  // The transformed tree is actually an extension of the original tree's
50
55
  // prototype chain. This allows the transformed tree to inherit any
51
56
  // properties/methods. For example, the `parent` of the transformed tree is
package/src/utilities.js CHANGED
@@ -30,6 +30,10 @@ export function castArrayLike(object) {
30
30
  * @param {any} object
31
31
  */
32
32
  export function getRealmObjectPrototype(object) {
33
+ if (Object.getPrototypeOf(object) === null) {
34
+ // The object has no prototype.
35
+ return null;
36
+ }
33
37
  let proto = object;
34
38
  while (Object.getPrototypeOf(proto) !== null) {
35
39
  proto = Object.getPrototypeOf(proto);
@@ -96,7 +100,7 @@ export function isStringLike(object) {
96
100
  return true;
97
101
  } else if (object?.toString === undefined) {
98
102
  return false;
99
- } else if (object.toString === getRealmObjectPrototype(object).toString) {
103
+ } else if (object.toString === getRealmObjectPrototype(object)?.toString) {
100
104
  // The stupid Object.prototype.toString implementation always returns
101
105
  // "[object Object]", so if that's the only toString method the object has,
102
106
  // we return false.