@weborigami/async-tree 0.5.7 → 0.6.0

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.
Files changed (178) hide show
  1. package/browser.js +1 -1
  2. package/index.ts +31 -35
  3. package/main.js +1 -2
  4. package/package.json +4 -7
  5. package/shared.js +77 -12
  6. package/src/Tree.js +8 -2
  7. package/src/drivers/AsyncMap.js +210 -0
  8. package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +36 -27
  9. package/src/drivers/{calendarTree.js → CalendarMap.js} +81 -62
  10. package/src/drivers/ConstantMap.js +30 -0
  11. package/src/drivers/DeepObjectMap.js +27 -0
  12. package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
  13. package/src/drivers/FileMap.js +245 -0
  14. package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
  15. package/src/drivers/ObjectMap.js +139 -0
  16. package/src/drivers/SetMap.js +13 -0
  17. package/src/drivers/{SiteTree.js → SiteMap.js} +16 -17
  18. package/src/drivers/SyncMap.js +245 -0
  19. package/src/jsonKeys.d.ts +2 -2
  20. package/src/jsonKeys.js +6 -5
  21. package/src/operations/addNextPrevious.js +35 -36
  22. package/src/operations/assign.js +30 -21
  23. package/src/operations/cache.js +29 -35
  24. package/src/operations/cachedKeyFunctions.js +1 -1
  25. package/src/operations/calendar.js +5 -0
  26. package/src/operations/clear.js +13 -12
  27. package/src/operations/constant.js +5 -0
  28. package/src/operations/deepEntries.js +23 -0
  29. package/src/operations/deepMap.js +9 -9
  30. package/src/operations/deepMerge.js +36 -25
  31. package/src/operations/deepReverse.js +23 -16
  32. package/src/operations/deepTake.js +7 -7
  33. package/src/operations/deepText.js +4 -4
  34. package/src/operations/deepValues.js +3 -6
  35. package/src/operations/deepValuesIterator.js +11 -11
  36. package/src/operations/delete.js +8 -12
  37. package/src/operations/entries.js +17 -10
  38. package/src/operations/filter.js +9 -7
  39. package/src/operations/first.js +12 -10
  40. package/src/operations/forEach.js +10 -13
  41. package/src/operations/from.js +31 -39
  42. package/src/operations/globKeys.js +22 -17
  43. package/src/operations/group.js +2 -2
  44. package/src/operations/groupBy.js +24 -22
  45. package/src/operations/has.js +7 -9
  46. package/src/operations/indent.js +2 -2
  47. package/src/operations/inners.js +19 -15
  48. package/src/operations/invokeFunctions.js +22 -10
  49. package/src/operations/isAsyncMutableTree.js +5 -12
  50. package/src/operations/isAsyncTree.js +5 -20
  51. package/src/operations/isMap.js +39 -0
  52. package/src/operations/isMaplike.js +34 -0
  53. package/src/operations/isReadOnlyMap.js +14 -0
  54. package/src/operations/isTraversable.js +3 -3
  55. package/src/operations/isTreelike.js +5 -30
  56. package/src/operations/json.js +4 -12
  57. package/src/operations/keys.js +17 -8
  58. package/src/operations/length.js +9 -8
  59. package/src/operations/map.js +27 -30
  60. package/src/operations/mapExtension.js +20 -16
  61. package/src/operations/mapReduce.js +22 -17
  62. package/src/operations/mask.js +31 -22
  63. package/src/operations/match.js +13 -9
  64. package/src/operations/merge.js +43 -35
  65. package/src/operations/paginate.js +26 -18
  66. package/src/operations/parent.js +7 -7
  67. package/src/operations/paths.js +8 -8
  68. package/src/operations/plain.js +6 -6
  69. package/src/operations/regExpKeys.js +21 -12
  70. package/src/operations/reverse.js +21 -15
  71. package/src/operations/root.js +6 -5
  72. package/src/operations/scope.js +31 -26
  73. package/src/operations/shuffle.js +23 -16
  74. package/src/operations/size.js +13 -0
  75. package/src/operations/sort.js +55 -40
  76. package/src/operations/sync.js +21 -0
  77. package/src/operations/take.js +23 -11
  78. package/src/operations/text.js +4 -4
  79. package/src/operations/toFunction.js +7 -7
  80. package/src/operations/traverse.js +4 -4
  81. package/src/operations/traverseOrThrow.js +13 -9
  82. package/src/operations/traversePath.js +2 -2
  83. package/src/operations/values.js +18 -9
  84. package/src/operations/withKeys.js +22 -16
  85. package/src/symbols.js +1 -0
  86. package/src/utilities/castArraylike.js +10 -2
  87. package/src/utilities/getMapArgument.js +38 -0
  88. package/src/utilities/getParent.js +2 -2
  89. package/src/utilities/isStringlike.js +7 -5
  90. package/src/utilities/setParent.js +7 -7
  91. package/src/utilities/toFunction.js +2 -2
  92. package/src/utilities/toPlainValue.js +22 -18
  93. package/test/SampleAsyncMap.js +34 -0
  94. package/test/browser/assert.js +20 -0
  95. package/test/browser/index.html +54 -21
  96. package/test/drivers/AsyncMap.test.js +119 -0
  97. package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +42 -23
  98. package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
  99. package/test/drivers/ConstantMap.test.js +15 -0
  100. package/test/drivers/DeepObjectMap.test.js +36 -0
  101. package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
  102. package/test/drivers/FileMap.test.js +185 -0
  103. package/test/drivers/FunctionMap.test.js +56 -0
  104. package/test/drivers/ObjectMap.test.js +166 -0
  105. package/test/drivers/SetMap.test.js +35 -0
  106. package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
  107. package/test/drivers/SyncMap.test.js +321 -0
  108. package/test/jsonKeys.test.js +2 -2
  109. package/test/operations/addNextPrevious.test.js +3 -2
  110. package/test/operations/assign.test.js +30 -35
  111. package/test/operations/cache.test.js +8 -6
  112. package/test/operations/cachedKeyFunctions.test.js +6 -5
  113. package/test/operations/clear.test.js +6 -27
  114. package/test/operations/deepEntries.test.js +32 -0
  115. package/test/operations/deepMerge.test.js +6 -5
  116. package/test/operations/deepReverse.test.js +2 -2
  117. package/test/operations/deepTake.test.js +2 -2
  118. package/test/operations/deepText.test.js +4 -4
  119. package/test/operations/deepValuesIterator.test.js +2 -2
  120. package/test/operations/delete.test.js +2 -2
  121. package/test/operations/extensionKeyFunctions.test.js +6 -5
  122. package/test/operations/filter.test.js +3 -3
  123. package/test/operations/from.test.js +23 -31
  124. package/test/operations/globKeys.test.js +9 -9
  125. package/test/operations/groupBy.test.js +6 -5
  126. package/test/operations/inners.test.js +4 -4
  127. package/test/operations/invokeFunctions.test.js +2 -2
  128. package/test/operations/isMap.test.js +15 -0
  129. package/test/operations/isMaplike.test.js +15 -0
  130. package/test/operations/json.test.js +2 -2
  131. package/test/operations/keys.test.js +16 -3
  132. package/test/operations/map.test.js +20 -18
  133. package/test/operations/mapExtension.test.js +6 -6
  134. package/test/operations/mapReduce.test.js +2 -2
  135. package/test/operations/mask.test.js +4 -3
  136. package/test/operations/match.test.js +2 -2
  137. package/test/operations/merge.test.js +15 -11
  138. package/test/operations/paginate.test.js +5 -5
  139. package/test/operations/parent.test.js +3 -3
  140. package/test/operations/paths.test.js +6 -6
  141. package/test/operations/plain.test.js +8 -8
  142. package/test/operations/regExpKeys.test.js +12 -11
  143. package/test/operations/reverse.test.js +4 -3
  144. package/test/operations/scope.test.js +6 -5
  145. package/test/operations/shuffle.test.js +3 -2
  146. package/test/operations/sort.test.js +7 -10
  147. package/test/operations/sync.test.js +43 -0
  148. package/test/operations/take.test.js +2 -2
  149. package/test/operations/toFunction.test.js +2 -2
  150. package/test/operations/traverse.test.js +4 -5
  151. package/test/operations/withKeys.test.js +2 -2
  152. package/test/utilities/setParent.test.js +6 -6
  153. package/test/utilities/toFunction.test.js +2 -2
  154. package/test/utilities/toPlainValue.test.js +51 -12
  155. package/src/drivers/DeepMapTree.js +0 -23
  156. package/src/drivers/DeepObjectTree.js +0 -18
  157. package/src/drivers/DeferredTree.js +0 -81
  158. package/src/drivers/FileTree.js +0 -276
  159. package/src/drivers/MapTree.js +0 -70
  160. package/src/drivers/ObjectTree.js +0 -158
  161. package/src/drivers/SetTree.js +0 -34
  162. package/src/drivers/constantTree.js +0 -19
  163. package/src/drivers/limitConcurrency.js +0 -63
  164. package/src/internal.js +0 -16
  165. package/src/utilities/getTreeArgument.js +0 -43
  166. package/test/drivers/DeepMapTree.test.js +0 -17
  167. package/test/drivers/DeepObjectTree.test.js +0 -35
  168. package/test/drivers/DeferredTree.test.js +0 -22
  169. package/test/drivers/FileTree.test.js +0 -192
  170. package/test/drivers/FunctionTree.test.js +0 -46
  171. package/test/drivers/MapTree.test.js +0 -59
  172. package/test/drivers/ObjectTree.test.js +0 -163
  173. package/test/drivers/SetTree.test.js +0 -44
  174. package/test/drivers/constantTree.test.js +0 -13
  175. package/test/drivers/limitConcurrency.test.js +0 -41
  176. package/test/operations/isAsyncMutableTree.test.js +0 -17
  177. package/test/operations/isAsyncTree.test.js +0 -26
  178. package/test/operations/isTreelike.test.js +0 -13
@@ -1,20 +1,17 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
 
3
3
  /**
4
- * Calls callbackFn once for each key-value pair present in the specific node of
5
- * the tree.
4
+ * Calls callbackFn once for each key-value pair present in the map.
6
5
  *
7
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
7
  *
9
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
10
9
  * @param {Function} callbackFn
11
10
  */
12
- export default async function forEach(treelike, callbackFn) {
13
- const tree = await getTreeArgument(treelike, "forEach");
14
- const keys = Array.from(await tree.keys());
15
- const promises = keys.map(async (key) => {
16
- const value = await tree.get(key);
17
- return callbackFn(value, key, tree);
18
- });
19
- await Promise.all(promises);
11
+ export default async function forEach(maplike, callbackFn) {
12
+ const map = await getMapArgument(maplike, "forEach");
13
+ for await (const key of map.keys()) {
14
+ const value = await map.get(key);
15
+ await callbackFn(value, key, map);
16
+ }
20
17
  }
@@ -1,33 +1,31 @@
1
- import DeepObjectTree from "../drivers/DeepObjectTree.js";
2
- import DeferredTree from "../drivers/DeferredTree.js";
3
- import FunctionTree from "../drivers/FunctionTree.js";
4
- import MapTree from "../drivers/MapTree.js";
5
- import ObjectTree from "../drivers/ObjectTree.js";
6
- import SetTree from "../drivers/SetTree.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import DeepObjectMap from "../drivers/DeepObjectMap.js";
3
+ import FunctionMap from "../drivers/FunctionMap.js";
4
+ import ObjectMap from "../drivers/ObjectMap.js";
5
+ import SetMap from "../drivers/SetMap.js";
7
6
  import * as symbols from "../symbols.js";
8
7
  import box from "../utilities/box.js";
9
8
  import isPlainObject from "../utilities/isPlainObject.js";
10
- import isUnpackable from "../utilities/isUnpackable.js";
11
- import isAsyncTree from "./isAsyncTree.js";
9
+ import setParent from "../utilities/setParent.js";
10
+ import isMap from "./isMap.js";
12
11
 
13
12
  /**
14
- * Attempts to cast the indicated object to an async tree.
13
+ * Attempts to cast the indicated object to a map.
15
14
  *
16
- * If the object is a plain object, it will be converted to an ObjectTree. The
15
+ * If the object is a plain object, it will be converted to an ObjectMap. The
17
16
  * optional `deep` option can be set to `true` to convert a plain object to a
18
- * DeepObjectTree. The optional `parent` parameter will be used as the default
17
+ * DeepObjectMap. The optional `parent` parameter will be used as the default
19
18
  * parent of the new tree.
20
19
  *
21
- * @typedef {import("../../index.ts").Treelike} Treelike
22
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
20
+ * @typedef {import("../../index.ts").Maplike} Maplike
23
21
  *
24
- * @param {Treelike | Object} object
25
- * @param {{ deep?: boolean, parent?: AsyncTree|null }} [options]
26
- * @returns {AsyncTree}
22
+ * @param {Maplike | Object} object
23
+ * @param {{ deep?: boolean, parent?: Map|AsyncMap|null }} [options]
24
+ * @returns {Map|AsyncMap}
27
25
  */
28
26
  export default function from(object, options = {}) {
29
27
  const deep = options.deep ?? object[symbols.deep];
30
- let tree;
28
+ let map;
31
29
  if (object == null) {
32
30
  throw new TypeError("The tree argument wasn't defined.");
33
31
  } else if (object instanceof Promise) {
@@ -35,29 +33,22 @@ export default function from(object, options = {}) {
35
33
  throw new TypeError(
36
34
  "The tree argument was a Promise. Did you mean to use await?"
37
35
  );
38
- } else if (isAsyncTree(object)) {
39
- // Argument already supports the tree interface.
40
- // @ts-ignore
36
+ } else if (isMap(object)) {
37
+ // Already a map
41
38
  return object;
42
39
  } else if (typeof object === "function") {
43
- tree = new FunctionTree(object);
44
- } else if (object instanceof Map) {
45
- tree = new MapTree(object);
40
+ map = new FunctionMap(object);
46
41
  } else if (object instanceof Set) {
47
- tree = new SetTree(object);
42
+ map = new SetMap(object);
48
43
  } else if (isPlainObject(object) || object instanceof Array) {
49
- tree = deep ? new DeepObjectTree(object) : new ObjectTree(object);
50
- } else if (isUnpackable(object)) {
51
- async function AsyncFunction() {} // Sample async function
52
- tree =
53
- object.unpack instanceof AsyncFunction.constructor
54
- ? // Async unpack: return a deferred tree.
55
- new DeferredTree(object.unpack, { deep })
56
- : // Synchronous unpack: cast the result of unpack() to a tree.
57
- from(object.unpack());
44
+ map = deep ? new DeepObjectMap(object) : new ObjectMap(object);
45
+ // @ts-ignore
46
+ } else if (object instanceof Iterator) {
47
+ const array = Array.from(object);
48
+ map = new ObjectMap(array);
58
49
  } else if (object && typeof object === "object") {
59
50
  // An instance of some class.
60
- tree = new ObjectTree(object);
51
+ map = new ObjectMap(object);
61
52
  } else if (
62
53
  typeof object === "string" ||
63
54
  typeof object === "number" ||
@@ -65,13 +56,14 @@ export default function from(object, options = {}) {
65
56
  ) {
66
57
  // A primitive value; box it into an object and construct a tree.
67
58
  const boxed = box(object);
68
- tree = new ObjectTree(boxed);
59
+ map = new ObjectMap(boxed);
69
60
  } else {
70
- throw new TypeError("Couldn't convert argument to an async tree");
61
+ throw new TypeError("Couldn't convert argument to a map");
71
62
  }
72
63
 
73
- if (!tree.parent && options.parent) {
74
- tree.parent = options.parent;
64
+ if (options.parent) {
65
+ setParent(map, options.parent);
75
66
  }
76
- return tree;
67
+
68
+ return map;
77
69
  }
@@ -1,31 +1,36 @@
1
- import ObjectTree from "../drivers/ObjectTree.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import ObjectMap from "../drivers/ObjectMap.js";
2
3
  import * as trailingSlash from "../trailingSlash.js";
3
- import getTreeArgument from "../utilities/getTreeArgument.js";
4
- import isAsyncTree from "./isAsyncTree.js";
4
+ import getMapArgument from "../utilities/getMapArgument.js";
5
+ import isMap from "./isMap.js";
5
6
  import merge from "./merge.js";
6
7
 
7
8
  const globstar = "**";
8
9
  const globstarSlash = `${globstar}/`;
9
10
 
10
- export default async function globKeys(treelike) {
11
- const globs = await getTreeArgument(treelike, "globKeys", { deep: true });
12
- return {
11
+ export default async function globKeys(maplike) {
12
+ const source = await getMapArgument(maplike, "globKeys", { deep: true });
13
+ return Object.assign(new AsyncMap(), {
13
14
  async get(key) {
14
15
  if (typeof key !== "string") {
15
16
  return undefined;
16
17
  }
17
18
 
18
- let value = await matchGlobs(globs, key);
19
- if (isAsyncTree(value)) {
19
+ let value = await matchGlobs(source, key);
20
+ if (isMap(value)) {
20
21
  value = globKeys(value);
21
22
  }
22
23
  return value;
23
24
  },
24
25
 
25
- async keys() {
26
- return globs.keys();
26
+ async *keys() {
27
+ for await (const key of source.keys()) {
28
+ yield key;
29
+ }
27
30
  },
28
- };
31
+
32
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
33
+ });
29
34
  }
30
35
 
31
36
  // Convert the glob to a regular expression
@@ -45,19 +50,19 @@ async function matchGlobs(globs, key) {
45
50
 
46
51
  // Collect all matches
47
52
  let matches = [];
48
- for (let glob of await globs.keys()) {
49
- if (glob === globstarSlash) {
53
+ for await (let glob of globs.keys()) {
54
+ if (glob === globstar || glob === globstarSlash) {
50
55
  // Remember for later
51
56
  globstarGlobs = await globs.get(glob);
52
57
  if (trailingSlash.has(key)) {
53
58
  // A key for a subtree matches the globstar
54
- matches.push(new ObjectTree({ [globstar]: globstarGlobs }));
59
+ matches.push(new ObjectMap({ [globstar]: globstarGlobs }));
55
60
  }
56
61
  } else if (matchGlob(glob, key)) {
57
62
  // Text matches glob, get value
58
63
  const globValue = await globs.get(glob);
59
64
  if (globValue !== undefined) {
60
- if (!isAsyncTree(globValue)) {
65
+ if (!isMap(globValue)) {
61
66
  // Found a non-tree match, return immediately
62
67
  return globValue;
63
68
  }
@@ -75,12 +80,12 @@ async function matchGlobs(globs, key) {
75
80
  } else {
76
81
  // Try globstar
77
82
  const globstarValue = await matchGlobs(globstarGlobs, key);
78
- if (!isAsyncTree(globstarValue)) {
83
+ if (!isMap(globstarValue)) {
79
84
  // Found a non-tree match, return immediately
80
85
  return globstarValue;
81
86
  } else if (trailingSlash.has(key)) {
82
87
  // No match but key is for subtree, return globstar tree
83
- return new ObjectTree({ [globstar]: globstarGlobs });
88
+ return new ObjectMap({ [globstar]: globstarGlobs });
84
89
  }
85
90
  }
86
91
  }
@@ -1,6 +1,6 @@
1
1
  import groupBy from "./groupBy.js";
2
2
 
3
- export default async function group(treelike, groupKeyFn) {
3
+ export default async function group(maplike, groupKeyFn) {
4
4
  console.warn("Tree.group() is deprecated. Use Tree.groupBy() instead.");
5
- return groupBy(treelike, groupKeyFn);
5
+ return groupBy(maplike, groupKeyFn);
6
6
  }
@@ -1,51 +1,53 @@
1
- import ObjectTree from "../drivers/ObjectTree.js";
2
- import getTreeArgument from "../utilities/getTreeArgument.js";
3
- import from from "./from.js";
4
- import isTreelike from "./isTreelike.js";
1
+ import SyncMap from "../drivers/SyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
3
+ import entries from "./entries.js";
4
+ import isMaplike from "./isMaplike.js";
5
5
  import values from "./values.js";
6
6
 
7
7
  /**
8
8
  * Given a function that returns a grouping key for a value, returns a transform
9
9
  * that applies that grouping function to a tree.
10
10
  *
11
- * @param {import("../../index.ts").Treelike} treelike
11
+ * @param {import("../../index.ts").Maplike} maplike
12
12
  * @param {import("../../index.ts").ValueKeyFn} groupKeyFn
13
13
  */
14
- export default async function groupBy(treelike, groupKeyFn) {
15
- const tree = await getTreeArgument(treelike, "groupBy");
14
+ export default async function groupBy(maplike, groupKeyFn) {
15
+ const source = await getMapArgument(maplike, "groupBy");
16
16
 
17
- const keys = Array.from(await tree.keys());
17
+ const result = new SyncMap();
18
+ const sourceEntries = await entries(source);
18
19
 
19
20
  // Are all the keys integers?
20
- const isArray = keys.every((key) => !Number.isNaN(parseInt(key)));
21
-
22
- const result = {};
23
- for (const key of await tree.keys()) {
24
- const value = await tree.get(key);
21
+ const integerKeys = sourceEntries.every(
22
+ ([key]) => !Number.isNaN(parseInt(key))
23
+ );
25
24
 
25
+ for (const [key, value] of sourceEntries) {
26
26
  // Get the groups for this value.
27
- let groups = await groupKeyFn(value, key, tree);
27
+ let groups = await groupKeyFn(value, key, source);
28
28
  if (!groups) {
29
29
  continue;
30
30
  }
31
31
 
32
- if (!isTreelike(groups)) {
32
+ if (!isMaplike(groups)) {
33
33
  // A single value was returned
34
34
  groups = [groups];
35
35
  }
36
- groups = from(groups);
37
36
 
38
37
  // Add the value to each group.
39
38
  for (const group of await values(groups)) {
40
- if (isArray) {
41
- result[group] ??= [];
42
- result[group].push(value);
39
+ let grouped = await result.get(group);
40
+ if (!grouped) {
41
+ grouped = integerKeys ? [] : new SyncMap();
42
+ result.set(group, grouped);
43
+ }
44
+ if (integerKeys) {
45
+ grouped.push(value);
43
46
  } else {
44
- result[group] ??= {};
45
- result[group][key] = value;
47
+ grouped.set(key, value);
46
48
  }
47
49
  }
48
50
  }
49
51
 
50
- return new ObjectTree(result);
52
+ return result;
51
53
  }
@@ -1,16 +1,14 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
 
3
3
  /**
4
- * Returns a boolean indicating whether the specific node of the tree has a
5
- * value for the given `key`.
4
+ * Returns true if the map has the given key, false otherwise.
6
5
  *
7
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
7
  *
9
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
10
9
  * @param {any} key
11
10
  */
12
- export default async function has(treelike, key) {
13
- const tree = await getTreeArgument(treelike, "has");
14
- const value = await tree.get(key);
15
- return value !== undefined;
11
+ export default async function has(maplike, key) {
12
+ const map = await getMapArgument(maplike, "has");
13
+ return map.has(key);
16
14
  }
@@ -1,6 +1,6 @@
1
1
  import toString from "../utilities/toString.js";
2
2
  import deepText from "./deepText.js";
3
- import isTreelike from "./isTreelike.js";
3
+ import isMaplike from "./isMaplike.js";
4
4
 
5
5
  const lastLineWhitespaceRegex = /\n(?<indent>[ \t]*)$/;
6
6
 
@@ -21,7 +21,7 @@ export default async function indent(strings, ...values) {
21
21
  }
22
22
  const { blockIndentations, strings: modifiedStrings } = modified;
23
23
  const valueTexts = await Promise.all(
24
- values.map((value) => (isTreelike(value) ? deepText(value) : value))
24
+ values.map((value) => (isMaplike(value) ? deepText(value) : value))
25
25
  );
26
26
  return joinBlocks(modifiedStrings, valueTexts, blockIndentations);
27
27
  }
@@ -1,29 +1,33 @@
1
+ import AsyncMap from "../drivers/AsyncMap.js";
1
2
  import * as trailingSlash from "../trailingSlash.js";
2
- import getTreeArgument from "../utilities/getTreeArgument.js";
3
- import isAsyncTree from "./isAsyncTree.js";
3
+ import getMapArgument from "../utilities/getMapArgument.js";
4
+ import isMap from "./isMap.js";
4
5
 
5
6
  /**
6
7
  * Return the interior nodes of the tree. This relies on subtree keys having
7
8
  * trailing slashes.
8
9
  *
9
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
10
- * @typedef {import("../../index.ts").Treelike} Treelike
10
+ * @typedef {import("../../index.ts").Maplike} Maplike
11
11
  *
12
- * @param {Treelike} treelike
12
+ * @param {Maplike} maplike
13
13
  */
14
- export default async function inners(treelike) {
15
- const tree = await getTreeArgument(treelike, "inners");
14
+ export default async function inners(maplike) {
15
+ const source = await getMapArgument(maplike, "inners");
16
16
 
17
- return {
17
+ return Object.assign(new AsyncMap(), {
18
18
  async get(key) {
19
- const value = await tree.get(key);
20
- return isAsyncTree(value) ? inners(value) : undefined;
19
+ const value = await source.get(key);
20
+ return isMap(value) ? inners(value) : undefined;
21
21
  },
22
22
 
23
- async keys() {
24
- const keys = [...(await tree.keys())];
25
- const subtreeKeys = keys.filter(trailingSlash.has);
26
- return subtreeKeys;
23
+ async *keys() {
24
+ for await (const key of source.keys()) {
25
+ if (trailingSlash.has(key)) {
26
+ yield key;
27
+ }
28
+ }
27
29
  },
28
- };
30
+
31
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
32
+ });
29
33
  }
@@ -1,21 +1,33 @@
1
- import from from "./from.js";
2
- import isAsyncTree from "./isAsyncTree.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
3
+ import isMap from "./isMap.js";
4
+
5
+ export default async function invokeFunctions(maplike) {
6
+ const source = await getMapArgument(maplike, "invokeFunctions", {
7
+ deep: true,
8
+ });
9
+
10
+ return Object.assign(new AsyncMap(), {
11
+ description: "invokeFunctions",
3
12
 
4
- export default async function invokeFunctions(treelike) {
5
- const tree = from(treelike);
6
- return {
7
13
  async get(key) {
8
- let value = await tree.get(key);
14
+ let value = await source.get(key);
9
15
  if (typeof value === "function") {
10
16
  value = value();
11
- } else if (isAsyncTree(value)) {
17
+ } else if (isMap(value)) {
12
18
  value = invokeFunctions(value);
13
19
  }
14
20
  return value;
15
21
  },
16
22
 
17
- async keys() {
18
- return tree.keys();
23
+ async *keys() {
24
+ for await (const key of source.keys()) {
25
+ yield key;
26
+ }
19
27
  },
20
- };
28
+
29
+ source: source,
30
+
31
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
32
+ });
21
33
  }
@@ -1,15 +1,8 @@
1
- import isAsyncTree from "./isAsyncTree.js";
1
+ import isReadOnlyMap from "./isReadOnlyMap.js";
2
2
 
3
- /**
4
- * Return true if the indicated object is an async mutable tree.
5
- *
6
- * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
7
- *
8
- * @param {any} obj
9
- * @returns {obj is AsyncMutableTree}
10
- */
11
- export default function isAsyncMutableTree(obj) {
12
- return (
13
- isAsyncTree(obj) && typeof (/** @type {any} */ (obj).set) === "function"
3
+ export default function isAsyncMutableTree(treelike) {
4
+ console.warn(
5
+ "Tree.isAsyncMutableTree() is deprecated, use Tree.isReadOnlyMap() instead, which returns the inverse."
14
6
  );
7
+ return !isReadOnlyMap(treelike);
15
8
  }
@@ -1,21 +1,6 @@
1
- /**
2
- * Return true if the indicated object is an async tree.
3
- *
4
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
5
- *
6
- * @param {any} obj
7
- * @returns {obj is AsyncTree}
8
- */
9
- export default function isAsyncTree(obj) {
10
- return (
11
- obj !== null &&
12
- typeof obj === "object" &&
13
- typeof obj.get === "function" &&
14
- typeof obj.keys === "function" &&
15
- // Regular JavaScript Map instances look like trees but can't have their
16
- // prototype chains extended (the way map() does), so exclude them.
17
- // Subclasses of Map that implement their own get() and keys() methods are
18
- // allowed, because they shouldn't have the same limitations.
19
- !(obj instanceof Map && Object.getPrototypeOf(obj) === Map.prototype)
20
- );
1
+ import isMap from "./isMap.js";
2
+
3
+ export default function isAsyncTree(treelike) {
4
+ console.warn("Tree.isAsyncTree() is deprecated, use Tree.isMap() instead.");
5
+ return isMap(treelike);
21
6
  }
@@ -0,0 +1,39 @@
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+
3
+ /**
4
+ * Return true if the indicated object is a Map instance or supports
5
+ * a `Map` compatible interface.
6
+ *
7
+ * @typedef {import("../../index.ts").SyncOrAsyncMap} SyncOrAsyncMap
8
+ *
9
+ * @param {any} object
10
+ * @returns {object is Map|AsyncMap}
11
+ */
12
+ export default function isMap(object) {
13
+ if (object instanceof Map || object instanceof AsyncMap) {
14
+ // Known positive cases
15
+ return true;
16
+ }
17
+
18
+ // Check for Map-like interface
19
+ if (
20
+ object &&
21
+ object.clear instanceof Function &&
22
+ object.delete instanceof Function &&
23
+ object.entries instanceof Function &&
24
+ object.forEach instanceof Function &&
25
+ object.get instanceof Function &&
26
+ object.has instanceof Function &&
27
+ object.keys instanceof Function &&
28
+ object.set instanceof Function &&
29
+ object.values instanceof Function &&
30
+ object.constructor.groupBy instanceof Function &&
31
+ "size" in object &&
32
+ (object[Symbol.iterator] instanceof Function ||
33
+ object[Symbol.asyncIterator] instanceof Function)
34
+ ) {
35
+ return true;
36
+ }
37
+
38
+ return false;
39
+ }
@@ -0,0 +1,34 @@
1
+ import isPlainObject from "../utilities/isPlainObject.js";
2
+ import isMap from "./isMap.js";
3
+
4
+ /**
5
+ * Returns true if the indicated object can be directly treated like a Map. This
6
+ * includes:
7
+ *
8
+ * - A function
9
+ * - An `Array` instance
10
+ * - An `AsyncMap` instance
11
+ * - An `Iterator` instance
12
+ * - A `Map` instance
13
+ * - A `Set` instance
14
+ * - A plain object
15
+ *
16
+ * Note: the `from()` method accepts any JavaScript object, but `isMaplike`
17
+ * returns `false` for an object that isn't one of the above types.
18
+ *
19
+ * @typedef {import("../../index.ts").Maplike} Maplike
20
+ *
21
+ * @param {any} object
22
+ * @returns {obj is Maplike}
23
+ */
24
+ export default function isMaplike(object) {
25
+ return (
26
+ object instanceof Array ||
27
+ object instanceof Function ||
28
+ // @ts-ignore
29
+ object instanceof Iterator ||
30
+ object instanceof Set ||
31
+ isMap(object) ||
32
+ isPlainObject(object)
33
+ );
34
+ }
@@ -0,0 +1,14 @@
1
+ import isMap from "./isMap.js";
2
+
3
+ /**
4
+ * Return true if the indicated object is a read-only map.
5
+ *
6
+ * @param {any} object
7
+ */
8
+ export default function isReadOnlyMap(object) {
9
+ if (!isMap(object)) {
10
+ return false;
11
+ }
12
+ // Respect readOnly if defined, otherwise assume read/write
13
+ return "readOnly" in object ? object.readOnly : false;
14
+ }
@@ -1,15 +1,15 @@
1
1
  import isPacked from "../utilities/isPacked.js";
2
- import isTreelike from "./isTreelike.js";
2
+ import isMaplike from "./isMaplike.js";
3
3
 
4
4
  /**
5
5
  * Return true if the object can be traversed via the `traverse()` method. The
6
- * object must be either treelike or a packed object with an `unpack()` method.
6
+ * object must be either maplike or a packed object with an `unpack()` method.
7
7
  *
8
8
  * @param {any} object
9
9
  */
10
10
  export default function isTraversable(object) {
11
11
  return (
12
- isTreelike(object) ||
12
+ isMaplike(object) ||
13
13
  (isPacked(object) && /** @type {any} */ (object).unpack instanceof Function)
14
14
  );
15
15
  }
@@ -1,33 +1,8 @@
1
- import isPlainObject from "../utilities/isPlainObject.js";
2
- import isAsyncTree from "./isAsyncTree.js";
1
+ import isMaplike from "./isMaplike.js";
3
2
 
4
- /**
5
- * Returns true if the indicated object can be directly treated as an
6
- * asynchronous tree. This includes:
7
- *
8
- * - An object that implements the AsyncTree interface (including
9
- * AsyncTree instances)
10
- * - A function
11
- * - An `Array` instance
12
- * - A `Map` instance
13
- * - A `Set` instance
14
- * - A plain object
15
- *
16
- * Note: the `from()` method accepts any JavaScript object, but `isTreelike`
17
- * returns `false` for an object that isn't one of the above types.
18
- *
19
- * @typedef {import("../../index.ts").Treelike} Treelike
20
- *
21
- * @param {any} obj
22
- * @returns {obj is Treelike}
23
- */
24
- export default function isTreelike(obj) {
25
- return (
26
- isAsyncTree(obj) ||
27
- obj instanceof Array ||
28
- obj instanceof Function ||
29
- obj instanceof Map ||
30
- obj instanceof Set ||
31
- isPlainObject(obj)
3
+ export default function isTreelike(treelike) {
4
+ console.warn(
5
+ "Tree.isTreelike() is deprecated, use Tree.isMaplike() instead."
32
6
  );
7
+ return isMaplike(treelike);
33
8
  }